Skip to content

Install

Edition: Community and Enterprise Offering: Self-managed, offline

sealfix installs from a signed offline bundle with Docker Compose. No step needs the internet on the host that runs it.

Prerequisites:

  • A Linux host with Docker Engine and Docker Compose v2.
  • The host's clock follows your organisation's NTP servers. The executor refuses to start when its clock and the metadata store's differ by more than 5 seconds.
  • A TLS reverse proxy you control (nginx, HAProxy, F5).
  • Your LDAP or Active Directory, or an OIDC identity provider.

1. Build and sign the bundle

On a machine with internet access, build the image and export it, with the pinned PostgreSQL image, into one bundle signed with your own cosign key:

make image
COSIGN_KEY=cosign.key scripts/release-bundle.sh bundle sealfix:<version> postgres:16
tar czf sealfix-deploy.tgz deploy/prod config/sealfix.example.yaml docs scripts/verify-bundle.sh

bundle/ holds the images, a CycloneDX SBOM, a signature for each and SHA256SUMS. Nothing uses a public transparency log, so everything works offline. Keep cosign.key offline and give cosign.pub to the operators by another channel.

2. Verify and load on the offline host

tar xzf sealfix-deploy.tgz
scripts/verify-bundle.sh bundle cosign.pub   # checksums and signatures; stop if it fails
docker load -i bundle/sealfix-images.tar

Note

Keeping a loaded image's digest needs Docker's containerd image store, the default on new Docker Engine installs. On the classic store, enable "containerd-snapshotter": true in /etc/docker/daemon.json before loading.

3. Configure

cd deploy/prod
for f in api executor meta; do cp "$f.env.example" "$f.env"; done
cp ../../config/sealfix.example.yaml sealfix.yaml
chmod 600 *.env
File Holds
meta.env Three strong passwords: the owner's, the API role's and the executor role's (openssl rand -base64 24)
api.env The API role's connection, the directory's service account, the bootstrap administrator's password hash
executor.env The executor role's connection, and one TARGET_*_DSN per target database
sealfix.yaml Everything else. No secret ever goes in this file

Create the bootstrap administrator's password hash:

docker compose run --rm --no-deps api sealfix hash-password

4. Start

docker compose up -d --pull never
docker compose ps

keygen and migrate exit with code 0; meta-pg, api and executor run and become healthy. The first start creates the executor's key, the schema and the database roles' grants.

Optional services

Each optional service is a Compose profile named after the section of sealfix.yaml it reads.

Profile Adds
notifications Tells each role what waits for it: e-mail, Teams, Slack, webhooks
git A signed record of every reviewed, approved and executed fix in your Git repository
siem The audit chain in your SIEM, as OCSF over syslog with TLS
docker compose --profile notifications up -d --pull never

To keep it on across restarts and upgrades, add COMPOSE_PROFILES=notifications to deploy/prod/.env.

5. TLS and access

The API listens on 127.0.0.1:8080. Put your TLS proxy in front of it and set FORWARDED_ALLOW_IPS in api.env to the proxy's address, so the audit log records the real client address.

  • Point the proxy's health check at /readyz. It answers 200 only when the metadata store answers, the executor's public key is loaded and the executor was seen in the last 3 minutes.
  • The session cookie is Secure: sign-in over plain HTTP does not work, by design.

6. Back up the executor's key

Do this now, before the first fix. Without the key, nothing sealed can ever be opened: pending fixes cannot run and executed fixes cannot be rolled back.

docker run --rm --user 0 -v sealfix_executor-keys:/keys:ro -v "$PWD":/backup \
  sealfix:<version> tar czf /backup/executor-keys.tgz -C /keys .

Store the archive offline, under your key-handling policy. Never copy it to the API's host.

Next

First-time setup: sign in as the bootstrap administrator at /login/local and walk the wizard.

Troubleshooting

The executor does not start: clock skew

The executor compares its clock with the metadata store's at start. Point both hosts at the same NTP servers, then check any host with sealfix doctor --clock.

Sign-in returns to the sign-in page

The session cookie is Secure. Reach sealfix through your HTTPS proxy, not on port 8080 directly.

The setup wizard says the executor key is not published yet

The API reads the public key from the executor-public volume. Check that the API container mounts it, and that keygen exited with code 0.

The notifier stopped after an upgrade

Optional services start only with their profile. Add COMPOSE_PROFILES=notifications (and git, siem if you run them) to deploy/prod/.env, then docker compose up -d.