Docker Compose (deprecated)
For a new install, use the Helm chart on Kubernetes. On one Linux host, follow Install on one host (k3s).
This page installs the Docker Compose stack in
docker/self-host/
on one Linux host. It stays supported for the rest of the 0.x releases.
From 1.0.0, the Helm chart is the only supported deployment.
Other setups (own reverse proxy, external database, Podman) can work but are unsupported.
The stack you get:
| Service | What it is |
|---|---|
reverse-proxy | Traefik — TLS through Let's Encrypt, routes /, /api, /webhooks |
webapp | React SPA served by nginx |
application-server | Spring Boot API |
application-worker | Same image, worker profile — runs the AI sandboxes. The only service given the Docker socket |
webhook-server | Same image, webhook profile — keeps receiving events while the app restarts |
postgres | PostgreSQL 18 + pg_partman (custom image — vanilla postgres:18 is not a drop-in) |
nats-server | NATS JetStream event buffer |
The compatibility policy states the supported deployment from 1.0.0 and what it means for your install.
Requirements
Supported host matrix
Hephaestus supports the specified single-host stack on the following native (not emulated) hosts:
| Distribution | CPU architecture |
|---|---|
| Ubuntu 24.04 LTS | x86-64 (amd64) |
| Ubuntu 24.04 LTS | ARMv8 64-bit (arm64) |
Both require the Docker Engine and Docker Compose versions in Tested versions. Docker supports these hosts according to its Ubuntu installation requirements. Before publication, each release passes qualification on both matrix cells.
Only the listed native hosts and the unmodified self-hosted Compose topology are supported. Other hosts, runtimes, and topologies are outside release qualification.
Tested versions
This table lists the versions that CI and release qualification actually run. A version that no job runs is not in the table.
| Component | Version | What tests it |
|---|---|---|
| Docker Engine | 28 or newer | Release qualification refuses an older engine on both matrix cells. It records the engine that it ran in the release assets host-smoke-amd64.json and host-smoke-arm64.json. No job runs version 28.0.0 itself. |
| Docker Compose | 2.24.4 or newer | Release qualification refuses an older Compose and records the version that it ran in the same assets. Every pull request checks that the Compose files render. No job runs version 2.24.4 itself. |
| PostgreSQL | 18, the release image with pg_partman | The merge queue runs the integration tests against an image built from the same Dockerfile before every change merges. |
| NATS server | The version that security/release-images.json pins | Release qualification boots it. The integration tests use NATS 2.10. |
| Traefik | The version that security/release-images.json pins | Release qualification sends a request through it. |
| Java | 21 | CI builds and tests with Java 21. The server image runs the Java 21 runtime of its buildpack. |
| Browsers | Chromium, the build that Playwright 1.63 bundles | The end-to-end, component, and Chrome extension suites run Chromium only. No job runs Firefox or Safari. |
| Chrome extension | Chrome 130 or newer | The extension manifest requires it. No job runs Chrome 130 itself. |
| Node.js, for release-lock verification | The version pinned in package.json#devEngines.runtime | CI installs this version through the setup-toolchain action. |
| Cosign, for release-lock verification | 3.1.4 | Release, promotion, and image signing jobs verify with this version. |
To see which Docker Engine and Docker Compose a release ran, download the host-smoke-*.json assets of that release from the releases page.
- The CPU, memory, and disk that Sizing gives.
- A host in the supported matrix, plus
gitandopenssl. - An NTP-synchronized clock. Clocks explains why.
- A DNS A record that points your hostname at the host.
Ports 80 and 443 must be reachable from the internet for
Let's EncryptHTTP-01, OAuth callbacks, and webhooks. - Outbound HTTPS to the destinations in Network egress.
- The Node.js version pinned in
package.json#devEngines.runtimeand Cosign for release-lock verification.
Clocks
Keep the clock of every host that runs a Hephaestus role or PostgreSQL synchronized by NTP. Conversation turn leases and person erasure compare times that different hosts write. If Slack does not answer when Hephaestus starts a streamed reply, the server looks for that reply in the thread by time. Hephaestus assumes that Slack's clock and the server's clock differ by less than 2 seconds. Conversation runtime § Slack replies has the rule.
1. Get the files
- Check out the newest version from the releases page:
VERSION=0.95.0 # the release you are installing, without the leading "v"
sudo git clone --depth 1 --branch "v$VERSION" \
https://github.com/hephaestus-build/Hephaestus.git /opt/hephaestus
sudo chown -R "$USER" /opt/hephaestus
cd /opt/hephaestus
cd docker/self-host
./setup.sh
cd ../..
node scripts/prepare-release-lock.ts "v$VERSION"
cd docker/self-host
setup.sh creates or updates .env with mode 0600.
It generates the database password and master encryption key (HEPHAESTUS_SECURITY_ENCRYPTION_KEY).
It copies the master key into an empty HEPHAESTUS_SECURITY_CREDENTIAL_ENCRYPTION_KEY.
It also generates these values:
- OAuth state-cookie key.
- Webhook secret.
- Routing secret.
- Message-broker credentials. It does not replace non-empty values and never prints a secret. Changing either encryption key on an existing installation makes everything encrypted under the old one unreadable.
Everything below runs from /opt/hephaestus/docker/self-host.
After step 2 supplies the remaining required values, docker compose --env-file .env --env-file release-lock.env config renders the effective configuration.
Before then it exits at the first missing required variable, as does every other docker compose subcommand in this directory.
See Troubleshooting.
2. Configure .env
HEPHAESTUS_SECURITY_ENCRYPTION_KEY protects the values that What the database seals lists.
HEPHAESTUS_SECURITY_CREDENTIAL_ENCRYPTION_KEY protects integration credentials. Loss of either key
makes the corresponding database values unreadable. Keep both with the backup set, in separate storage with separate credentials, as Keep the required keys outside the cluster describes. Rotate the
credential key only with the credential-key rotation procedure.
setup.sh manages the internal secrets listed above.
-
Open
.env. -
Fill in the remaining REQUIRED values.
-
Do not replace the generated internal secrets manually.
Variable What / how APP_HOSTNAMEYour public hostname, for example, hephaestus.example.comACME_EMAILLet's Encryptexpiry noticesOne login provider GitHub OAuth ( GH_OAUTH_CLIENT_ID/_SECRET) or GitLab OAuth (GITLAB_OAUTH_CLIENT_ID/_SECRETand its base URL), from First steps § 1. One is enough.DOCKER_GROUP_IDHost group ID that can access /var/run/docker.sock. Use the command below. A wrong value prevents every sandbox start.HEPHAESTUS_AUTH_BOOTSTRAP_ADMINSFrom First steps § 2 — set it before first boot -
To find
DOCKER_GROUP_ID, run:stat -c %g /var/run/docker.sockOther generated values also affect the lifecycle:
- A database password change does not update an initialized PostgreSQL volume.
- State-cookie key rotation invalidates OAuth flows in progress.
- Webhook secret rotation requires the same change at each configured provider. The GitLab routing secret is never entered anywhere. GitLab group hooks stop delivery until registration repeats if you rotate the secret incorrectly.
-
Rotate it as production setup describes.
-
Back up
.envwith the database as Backup & Restore describes.
Serving more than one hostname
An instance answers on APP_HOSTNAME.
It can also answer on other names, such as a domain you are leaving or a www. variant.
The reverse proxy reads APP_HOST_MATCH as a host matcher.
It replaces the default rather than extending it.
-
List all required hostnames in
APP_HOST_MATCH. -
Include
APP_HOSTNAMEin the matcher:APP_HOST_MATCH=Host(`hephaestus.example.com`) || Host(`old.example.com`)Traefik takes one hostname per
Host(), joined with||.Host(`a`,`b`)is rejected and the router does not load at all. -
Before deployment, point each name at this host in DNS.
The certificate covers exactly the names in the matcher. Certificate issuance requires a challenge response on each name. A name that does not yet resolve here delays the whole certificate. The first name becomes the certificate's subject. The other names become its alternative names.
APP_HOSTNAME remains the instance's single origin.
The SPA, API, and auth issuer all use it.
The proxy redirects a browser that opens any other name to APP_HOSTNAME.
The app therefore loads from only one host.
OAuth callback URLs stay on APP_HOSTNAME.
The API and webhook endpoints deliberately answer on all names in the matcher. An open tab and a provider that still posts to the old name continue to work through the move. The proxy redirects only what a browser loads.
The login callback uses the address where the request arrived.
A sign-in started at /api/login/... on another name asks the provider to return to that name.
The provider refuses because its only registered callback uses APP_HOSTNAME.
Normal access avoids this problem: the proxy redirects the browser before sign-in starts.
- Register callbacks for
APP_HOSTNAMEonly.
A callback for another name would permit a session outside the canonical origin.
The proxy controls both the matcher and the redirect.
A proxy that does not read this stack's routing configuration can send requests for any names.
In that setup, APP_HOST_MATCH has no effect.
- Configure the extra names on that proxy.
- Configure their certificate there.
- Configure its redirect to
APP_HOSTNAME.
3. First boot
docker compose --env-file .env --env-file release-lock.env up -d
docker compose --env-file .env --env-file release-lock.env logs -f database-migration application-server
First startup pulls about 2 GB of images, and the one-shot database-migration service runs all database migrations.
The application server starts after it completes. A failed migration shows in its log.
It can take a few minutes.
Continue with First steps § 3. Sign in.
Upgrades
Upgrade owns the upgrade procedure, failed-migration recovery, rollback, update checks, and the release history. Troubleshooting lists the common failures of an installation and their fixes.