My App

Self-hosting and operations

Installation modes, secrets, TLS, upgrades, and recovery.

Installation modes

The installer supports two modes.

ModeWhen to use itImage source
GitHub source buildFirst evaluation or a simple single-manager installationBuilds the public mhbdev/upstand repository on the manager
Release imagesRepeatable production rolloutImmutable GHCR image digests supplied through UPSTAND_*_IMAGE

Use release images for a production fleet once a release is published. Source builds are intentionally pinned to the checked-out Git commit and stay on the manager node.

The installer is non-interactive by default. It accepts omitted origins for a first-run evaluation and derives temporary nip.io HTTPS origins from a public IPv4 manager. Pass --interactive to prompt for missing API and dashboard origins. For a production rollout, set real HTTPS origins explicitly and rerun the installer after DNS is configured.

Installer contract

The installer accepts --help and --interactive. It validates immutable image digests, HTTPS origins, Docker Swarm manager access, and the attachable overlay network before deployment. It writes only non-secret configuration to /etc/upstand/.env; generated passwords and encryption material are separate mode-0600 files under /etc/upstand/secrets/. Re-running the installer is the supported upgrade and repair path.

For a fully non-interactive release deployment, provide BETTER_AUTH_URL, CORS_ORIGIN, NEXT_PUBLIC_SERVER_URL, and the three UPSTAND_*_IMAGE variables as immutable digests. Set UPSTAND_BUILD_FROM_SOURCE=true only when intentionally building a pinned Git revision on the manager.

Secrets and state

/etc/upstand/.env is mode 0600 and contains origins and immutable image references. The installer stores database, Redis, authentication, and encryption material as individual mode 0600 Docker secret files under /etc/upstand/secrets/. Back up both locations securely. Losing the SSH-key secret means stored SSH-key and credential secrets cannot be decrypted.

Persistent Docker volumes hold Postgres and Redis. They are local volumes, so do not move the upstand.control-plane=true label to another machine without migrating state.

If UpGal is enabled, provider credentials are encrypted with the configured application encryption key and stored in Postgres. Back up that key with the database and .env; changing or losing it without a migration plan makes existing AI provider credentials unusable. External MCP keys are hashed and can be revoked from Settings → AI.

Session and sign-in security

Upstand uses Better Auth email/password sessions. In production, session cookies are HttpOnly, Secure, and SameSite=Lax, and are scoped only to the sibling dashboard/API domain required by the installation. The API validates the configured dashboard and API origins, and protected API procedures reject requests without a valid session or an explicitly scoped API key. Sessions expire after seven days and rotate after a day of activity; session records are persisted in Postgres and the active session cache is kept in Redis.

Enable 2FA for every administrator from Settings → Security & 2FA. API keys do not create browser sessions and cannot access authentication, 2FA, terminal, or other session-only flows. Keep the Better Auth secret, Redis password, and database password private; rotate them only through the documented migration process because changing encryption material can make existing encrypted credentials unreadable.

TLS and DNS

Caddy obtains certificates only after DNS points at the manager and port 80 is reachable. If issuance fails:

  1. Confirm dig +short app.example.com and dig +short api.example.com return the manager IP.
  2. Confirm no other process owns ports 80 or 443: ss -ltnp '( sport = :80 or sport = :443 )'.
  3. Inspect docker logs upstand-caddy --tail 100.

Use real domains in production. nip.io is useful only for a temporary validation environment.

Backup and recovery

Back up /etc/upstand/.env and the Postgres volume before upgrades. Configure an S3 destination in the dashboard before relying on scheduled database or volume backups. A restore overwrites its target, so test it in a disposable resource first.

Docker cleanup can be enabled for the control plane in Web Server settings and for each registered remote server on the Remote Servers page. The daily job runs separate argument-safe Docker prune operations at the control-plane local 03:00 hour; remote cleanup uses the server's registered SSH key and does not join or expose the remote Docker socket.

Security checklist

  • Use a unique owner password and enable 2FA.
  • Keep port 22 limited to administrators.
  • Restrict Swarm ports to private node networks; never expose 2377, 7946, or 4789 broadly to the internet.
  • Keep the dashboard/API behind HTTPS and do not publish database ports unless a resource explicitly requires it.
  • Rotate Swarm join tokens after a node is lost or an operator leaves.

On this page