Skip to content

For IT Admins

This section is for the operator who runs a Doable host: provisioning, keeping services up, rotating secrets, restoring backups, responding to incidents. Sysadmins, SREs, DevOps, platform engineers.

If you instead have admin rights inside the Doable web app, see Platform Admin. The two roles often overlap; the tools differ.

Two tools you use every day

  1. doable, a Rust TUI that runs on your laptop. Provisions fresh servers, opens an admin TUI against local Postgres, or tunnels into a remote server over SSH. Recommended entry point for routine work.
  2. SSH into the doable tmux session on the server. Three windows (api, web, ws) stream live logs; stop and restart a process with Ctrl-C and re-run.

The TUI is faster for guided tasks (toggle a flag, edit cloudflared, rotate the DB password). The tmux session is faster when you need to watch output while changing something.

Architecture at a glance

A single Doable host is four processes:

  • web: Next.js 15 on 127.0.0.1:3000
  • api: Hono on 127.0.0.1:4000
  • ws: WebSocket / Yjs collaboration on 127.0.0.1:4001
  • PostgreSQL 16: Unix socket + 127.0.0.1:5432 (peer auth by default)

Everything binds to 127.0.0.1. The only public entry is a Cloudflare Tunnel running as the cloudflared systemd service. It dials out to Cloudflare, which proxies traffic back through the tunnel to Caddy, then to the right local port. UFW drops everything except SSH on :22.

                                            ┌────────────────────────┐
                                            │  Cloudflare edge       │
   Internet ────────────────────────────► │  (handles TLS, DNS)     │
                                            └──────────┬─────────────┘
                                                       │ outbound tunnel
                                            ┌──────────▼─────────────┐
                                            │  cloudflared (systemd) │
                                            └──────────┬─────────────┘
                                            ┌──────────▼─────────────┐
                                            │  Caddy on 127.0.0.1    │
                                            │  (reverse proxy)       │
                                            └──┬─────────┬───────────┘
                                               │         │
                              ┌────────────────┘         └─────────────┐
                              ▼                                        ▼
                   ┌────────────────────┐                  ┌──────────────────┐
                   │ web   127.0.0.1:3000│                  │ api  127.0.0.1:4000│
                   │ ws    127.0.0.1:4001│                  │ Postgres :5432    │
                   └────────────────────┘                  └──────────────────┘

There is no public port. There is no LoadBalancer. There is no Kubernetes. This is a deliberate design choice: fewer moving parts, fewer pages.

Workflow cheat sheet

When What Where to read
Day 1: provision a new server doable install from your laptop doable-cli Reference
Day 2: add a member, rotate DB password doable admin doable-cli Reference
Day 2: edit .env, reload cloudflared doable admin, Server Config doable-cli Reference
Day 2: daily backup pg_dump cron + off-host sync Operations Runbook
Day 2: apply a schema migration pnpm db:migrate Operations Runbook
Day 2: pull new code, restart web git pull + rebuild + tmux restart Operations Runbook
Incident: web is 502 Check tmux windows + systemd + tunnel Operations Runbook
Incident: DB connection refused Check pg_isready, peer auth, .env Operations Runbook
QA: run multi-agent test campaign Disable rate limits, restore after Operations Runbook
New feature: wildcard DNS for /admin Cloudflare API token + admin panel Operations Runbook

What's secure-by-default

After doable install finishes, the host has:

  • All services bound to 127.0.0.1 only (verifiable with ss -tlnp)
  • UFW firewall enabled, deny-all inbound except SSH/22
  • fail2ban watching the systemd journal for SSH brute-force
  • PostgreSQL listening on localhost only, peer auth where possible
  • Caddy auto-managing TLS (the public certs come from Cloudflare, not Caddy)
  • Random 32-byte secrets for JWT_SECRET, ENCRYPTION_KEY, DB password
  • A 2 GB swap file so OOM doesn't take the host down on small VPS plans
  • A platform-admin user (you, by email) seeded into the database

You did not have to make any of these choices. If you want to tighten further, see Hardening and the Operations Runbook security checklist.

  • doable-cli Reference: every flag, every screen, every keystroke of the doable binary. Start here if you've never run the TUI before.
  • Operations Runbook: backups, migrations, scaling, certificate rotation, incident response. Bookmark this; you'll open it more than once.
  • Bare-metal deployment: the network diagram and the underlying deployment/server-setup.sh walked line by line.
  • Security model: what we encrypt, what we hash, what we leave in plaintext, and why.