Docs · Get started
Install on your own server
One command writes a Docker Compose stack to your server, starts it, runs the migrations and checks that everything answers. The admin and the gateway run signed, prebuilt release images.
What you get
A Docker Compose stack in ./.caelo/ with four services:
- Postgres 16 with both Caelo databases.
- Caddy, which fetches TLS certificates from Let's Encrypt and serves the live site on
<domain>(with/api/*going to the gateway), the admin onadmin.<domain>and the staging site on its own host withX-Robots-Tag: noindex. - The admin and the API gateway, from
ghcr.io/caelo-cms/adminandghcr.io/caelo-cms/gateway.
The images are pinned by digest and verified with cosign against Caelo's release workflow before anything is written. You never build an image, and a docker compose pull cannot change the release behind your back.
Requirements
- A Linux server with Docker Engine and the Compose plugin.
- Bun 1.3.13 or newer.
- cosign on the
PATH. Without it the install stops before writing anything. - Ports 80 and 443 reachable from the internet, and DNS records for three hosts pointing at the server:
<domain>,admin.<domain>andstaging.<domain>(or the host you pass as--staging-domain). - Enough memory: the admin container reserves 2 GiB for the quality checks, on top of Postgres and Caddy.
- For installing updates from the admin: systemd, and a non-root user in the
dockergroup to run the install as.
Install
Run this from the directory that should hold the install:
bunx @caelo-cms/provisioning@latest init --provider self-hosted \
--domain example.com \
--owner-email you@example.com
The same command without init does the same thing. In order, it:
- resolves the admin and gateway images of its own Caelo version to digests and cosign-verifies them. A failed check stops here, and nothing is written.
--version x.y.zinstalls another release; - generates the secrets and writes them to
.caelo/config.json, and records the verified digests in.caelo/release.json; - writes
.caelo/docker-compose.ymland.caelo/Caddyfile; - on Linux with systemd, sets up the update agent (see Updates and maintenance);
- prints the owner setup link, valid for 24 hours;
- pulls the images, starts Postgres, runs the migrations, starts everything and waits for the admin and gateway health checks;
- prints the DNS hosts to point at the server and the setup link again.
Open the setup link (https://admin.<domain>/setup?token=…) to create the owner account, then choose your AI provider. See the Quickstart.
If a step fails
The installer names the step and the reason. Fix the cause and run, from the same directory:
bunx @caelo-cms/provisioning@<version> up
up re-checks the database roles, re-writes the Compose file and the Caddyfile, and brings the stack up from wherever it stopped. Run it too after you change anything in .caelo/config.json. The setup link is printed before the stack starts, so a failed start does not lose it.
Options
| Flag | Use it when |
|---|---|
--tls internal | The server has no public DNS (a LAN box, an evaluation install). Caddy issues certificates from its own local CA; browsers warn until you trust its root. |
--http-port, --https-port | Ports 80 and 443 are taken. Let's Encrypt then only works if another proxy forwards those hosts to Caddy, so combine them with --tls internal or such a proxy. |
--staging-domain | Staging should run on another host than staging.<domain>. |
--version x.y.z | You want a specific release. |
--skip-verify | Only for forks that publish unsigned images. Never on a production install. |
Files that matter
.caelo/config.json(mode 600): database passwords and the key that encrypts stored AI provider keys. Backups do not contain it. Keep a copy somewhere safe..caelo/release.json: the running release, its image digests and the upgrade history..caelo/docker-compose.ymland.caelo/Caddyfile: generated.uprewrites them.
Day-to-day
| Task | Command |
|---|---|
| Restart | docker compose -f .caelo/docker-compose.yml restart |
| Logs | docker compose -f .caelo/docker-compose.yml logs -f caelo-admin caelo-gateway caddy |
| Container health | docker compose -f .caelo/docker-compose.yml ps |
| Apply migrations by hand | bunx @caelo-cms/provisioning@<version> migrate |
| Upgrade, back up, restore | See Updates and maintenance |
Redirects that Caelo records are answered by Caddy. Each build carries a redirects file that Caddy imports and reloads within seconds of a deploy.
Limits
- One server, one Postgres. There is no failover and no read replica.
- Backups are point-in-time archives (
pg_dumpplus files), not continuous WAL archiving. Schedule them yourself. - There is no
rotate-secretordestroycommand for self-hosted installs yet.
Common problems
- "cosign is required … was not found on PATH". Install cosign and re-run.
- "cosign verify FAILED". The image was not signed by Caelo's release workflow. Do not continue. Retry once in case of a Sigstore outage.
- "Could not resolve ghcr.io/caelo-cms/admin:<version>". The version does not exist (check the releases) or GHCR is unreachable.
- Certificates are not issued. The three hosts must resolve to the server and ports 80 and 443 must be reachable.
- An install made with an older release never came up. Run
bunx @caelo-cms/provisioning@latest uponce from the directory that holds.caelo/. It rewrites the Compose file and the Caddyfile and removes services older releases referenced. The admin is then onadmin.<domain>.
Something wrong or missing on this page? Open an issue on GitHub