Agency playbook: host client apps on Girder.

You sell hosting and deploys. Girder is the machine under that business. This is how to do it on Girder Standard today, without multi-user accounts, and what Platform Edition will add later.

The model

Girder Standard trusts one operator: you. The API token and full dashboard are root. Clients should never see them.

What clients get instead:

  • Their app(s) running on HTTPS with their domains.
  • A git remote they can push to only if you give them a key locked to their apps (and usually only if they are on your Tailscale or you push for them).
  • You as the person who restores, resizes, and bills.

That is a private agency PaaS, not a public Heroku clone. Public self-serve signup is not the goal of this playbook.

Hard rules

  • Never share the API token (girder api-token). It is root on the whole instance.
  • Never give a git key with no ACL. A key without an ACL file has full access. Always set an allow-list of app names.
  • Never put the dashboard or git on the public internet for Standard ops. Keep them on Tailscale (or another private network). Only app traffic is public on 443.
  • One client, one app name prefix or stack so you can find and restore the right thing under pressure.
  • You own the backup story. Untested backups are a rumor. Run verify before you promise RTO to a client.

Instance setup (once)

  1. Install Girder on your control node (see docs). Wire Cloudflare for DNS/TLS and Tailscale for ops.
  2. Set appsDomain to something you own, e.g. apps.youragency.com. Wildcard client hosts live under it until you attach custom domains.
  3. Turn on nightly backups and point offsite storage when you can (R2 or equivalent). Region-local-only backups are a known risk; treat offsite as part of the agency offering.
  4. Optionally add a data node and app nodes as you grow. Multi-node is supported without changing the client workflow.
  5. Install your license; keep the license file and secrets offline-backed-up like production data.

Per-client layout

Suggested naming:

$ girder apps create acme-web
$ girder apps create acme-worker   # if they need a queue consumer
# group related processes under one stack name in app config
$ girder apps config acme-web stack acme
$ girder apps config acme-worker stack acme

Provision services per app (or share with --from when a worker must use the same database as the web process):

$ girder services add acme-web postgres
$ girder services add acme-web redis
$ girder services add acme-web s3
$ girder services add acme-worker postgres --from acme-web

Staging: enable built-in staging on apps that need a durable pre-prod lane. Previews are for ephemeral branch noise; staging is for “show the client before go-live.”

Keys and ACLs

For each person or CI that may push:

$ girder keys add acme-ci ./acme-ci.pub
$ girder keys allow acme-ci acme-web acme-worker   # only these apps
$ girder keys list                                   # shows fingerprint + allowed apps

The key’s ACL file lists allowed app names. An empty ACL file denies everything. No ACL file means full access (operator default; also keys allow <name> --all). Prefer one key per client automation, not one key for all clients.

Git is Tailscale-only by design. Options for client deploys:

  • You push from your laptop or CI that sits on the tailnet (simplest, most control).
  • Client CI on the tailnet with a scoped key (self-hosted runner or Tailscale in the job).
  • You pull from their GitHub and push to Girder (no client access to your origin).

Do not open port 2222 to the world on Standard without a written security review. Platform Edition plans optional public git later, with rate limits and 2FA on any public dashboard.

Domains

$ girder domains add acme-web www.acme.example
$ girder domains add acme-web acme.example

App hosts can sit behind Cloudflare proxy (CDN and DDoS). Download hosts for bucket file serving stay direct when you use the download kind. Probe domains from the dashboard after cutover so you see origin vs edge mistakes early.

Data and staging

  • Each app gets its own Postgres database, Redis, and S3 bucket when you provision them. That is your tenant boundary for app data.
  • It is not a tenant boundary for the control plane. Anyone with the API token or an unrestricted key can touch everything.
  • Use staging seed carefully with production PII; scrub when the client requires it.
  • Env vars: set via CLI/dashboard; the API never returns secret values, only key names. Still, dashboard access is operator-only.

Backups you can sell

  • Nightly restic + per-database dumps + optional WAL-G PITR.
  • backup restore <app> lands in a scratch database; backup swap promotes. Live data is not overwritten by accident.
  • Run backup verify / scheduled drills and keep a short note of the last green restore date for client contracts.
  • Staging and preview databases are skipped from backup coverage on purpose; do not promise restore of throwaway envs.

When something goes wrong, girder support-bundle (or the dashboard Config action) builds a redacted archive for vendor support without shipping secrets.

Your billing (not Girder’s)

You invoice clients for hosting, deploys, and retainers. Girder is your cost of goods (license + Hetzner + Cloudflare). There is no per-client meter inside Standard. Track clients in your own tools; name apps clearly so usage conversations stay grounded.

Platform Edition will add usage export for that conversation. It will not replace your Stripe or contracts.

What Platform Edition adds

Platform Edition is a paid license tier on the same binary: multi-user login, roles, app grants, scoped API tokens, quotas, and later white-label and optional public dashboard (with 2FA). See the product plan in the repo: docs/PLATFORM-EDITION.md.

Until that ships, this playbook is the supported agency path. If shared root access is already painful, email [email protected] and say you want Platform design-partner access.

Related: migrate a client off Heroku, how Girder compares, pricing.