Skip to content
AgentDock

Self-hosting

Backups and upgrades

What to back up, the encryption key, migrations and retention.

Running AgentDock day to day comes down to four things: back up PostgreSQL and the encryption key, rebuild the images to upgrade, know what is deleted on its own, and know where to look when something is unhealthy. The commands below assume the Docker Compose stack in ~/stacks/agentdock.

What to back up

WhatWhereWhy
PostgreSQLthe pgdata volumeEverything AgentDock knows: accounts, projects, runners, history, sessions, cost, settings, audit. This is the source of truth.
APP_ENCRYPTION_KEYenv/api.envDecrypts the stored secrets (Telegram bot token, webhook and trigger secrets, GitHub App key). Back it up separately from the database.
.env and env/*.envthe stack directoryDatabase and Redis passwords, the tunnel token, your settings.
docker-compose.yml, Caddyfilethe stack directorySmall, but you will want them.

Redis does not need a backup. It only holds BullMQ timers for cron schedules, PostgreSQL stays the source of truth, and a reconciler rebuilds lost timers on start.

Nothing on the agent machines needs a server-side backup either. Each runner keeps its token in ~/.config/agentdock/runner.json; if that file is lost, pair the machine again.

Dump the database

Terminal
cd ~/stacks/agentdock
docker compose exec -T postgres pg_dump -U postgres -Fc agentdock > agentdock-$(date +%F).dump

Run it from cron and copy the dumps off the host. The custom format (-Fc) is compressed and restores with pg_restore.

Restore

Restore into an empty database with the API stopped, then start the API again. It applies any newer migrations on start.

Terminal
cd ~/stacks/agentdock
docker compose stop api web proxy
docker compose exec -T postgres dropdb -U postgres agentdock
docker compose exec -T postgres createdb -U postgres agentdock
docker compose exec -T postgres pg_restore -U postgres -d agentdock --no-owner < agentdock-2026-10-01.dump
docker compose up -d

The restored instance needs the same APP_ENCRYPTION_KEY as the one the dump was taken from. With a different key the API starts, but every stored secret fails to decrypt and has to be entered again.

Upgrades

There is no published image, so an upgrade is: update the checkout, rebuild both images under the same tags, and recreate the containers.

Terminal
cd ~/src/AgentDock
git pull
 
docker build -t agentdock-api:local -f apps/api/Dockerfile .
docker build -t agentdock-web:local -f apps/web/Dockerfile \
  --build-arg NEXT_PUBLIC_LIVE_URL=wss://agentdock.example.com/api/live \
  --build-arg API_URL=http://api:8180 \
  .
 
cd ~/stacks/agentdock
docker compose up -d
docker image prune -f

What happens on start:

  1. The API entrypoint runs prisma migrate deploy, then prisma db seed, then starts the server. The seed is idempotent: it upserts settings and adds the price table only when none exists, so your price edits survive.
  2. If a migration fails, the API refuses to start and stays unhealthy. The web app and the proxy wait for a healthy API, so the site is down until you fix it. Read docker compose logs api.
  3. Migrations only go forward. Take a dump before every upgrade; rolling back means restoring it and rebuilding the previous commit.

To pin a version, build from a specific commit instead of the latest one (git checkout <commit>). Compare the new .env.example files with yours after pulling: new variables are documented there.

Upgrading runners

Runners upgrade on their own machines with the tool that installed them, then restart:

Terminal
brew upgrade agentdock-runner && brew services restart agentdock-runner
# or
npm i -g agentdock-runner@latest && systemctl --user restart agentdock-runner

The runner and the API speak a versioned protocol. If they no longer match, the API closes the connection with code 4400 and the runner exits with code 78, which the service does not restart. Upgrade the side that is behind.

Retention

Some tables are cleaned up by jobs inside the API. The rest grow until you act.

DataKeptJob
Activity feed items180 days (ACTIVITY_RETENTION_DAYS)daily at 04:30
Outbound webhook deliveries and inbound trigger deliveries30 daysdaily at 04:45
GitHub App webhook deliveries14 daysdaily at 04:50
BullMQ jobs in Redis24 hours when completed, 7 days when failedBullMQ
Audit recordsnever deletednone
Events, LLM requests, sessions, runs, issue cacheno automatic deletionnone

The audit log is append-only by design. A database trigger rejects UPDATE, DELETE and TRUNCATE on audit_records, and every record is hash-chained to the previous one. A nightly job (AUDIT_VERIFY_CRON, default 03:00) verifies the chain, and Admin → Audit shows the result. Admin → Schedules lists these system jobs next to the projects' schedules. Job times are server time.

Plan disk space for the tables without retention. Sessions and LLM requests are the largest on a busy fleet.

Health checks

CheckCommandHealthy answer
API, from inside the stackGET http://api:8180/health200 with {"status":"ok","timestamp":"..."}
API, through the proxycurl https://agentdock.example.com/api/healththe same
Web appGET http://web:3517/any status below 500 (signed-out pages redirect)
Containersdocker compose psapi and web show healthy
A runneragentdock-runner status on its machinepaired: yes, reach: reachable

/health is public and says only that the process is up. It does not check the database. For the fleet itself, Admin → Runners shows each runner's status with a remedy when it is offline, and admins get a runner.offline notification after a runner has been gone for more than 5 minutes.

Logs

  • docker compose logs -f api for the API: migrations, workers, runner connections.
  • docker compose logs -f web and docker compose logs -f proxy for the rest of the stack.
  • On agent machines, see Runner logs.

Next steps