Skip to content

Troubleshooting

Start by preserving logs and the current release/image information before changing services:

Terminal window
koakademy status
docker service ps --no-trunc koakademy_app
docker service logs --tail=200 koakademy_app
docker service logs --tail=100 koakademy_caddy
docker service logs --tail=100 koakademy_postgres

Never publish Docker Secrets, database dumps, student data, or unredacted production logs.

Confirm the domain resolves to the VPS and ports 80/443 are free. Caddy needs both ports to obtain and renew certificates:

Terminal window
ss -ltnp '( sport = :80 or sport = :443 )'
docker service logs --tail=200 koakademy_caddy

The installer fails rather than overwriting legacy koakademy-* services. Follow Deployment on a fresh host or during a planned migration window.

Check the migration job, private dependencies, and application task:

Terminal window
docker service ps --no-trunc koakademy-migrate-<timestamp>
docker service ps --no-trunc koakademy_postgres
docker service ps --no-trunc koakademy_redis
docker service ps --no-trunc koakademy_gotenberg
docker service logs --tail=200 koakademy_app

Common causes are a failed migration, insufficient memory, a full volume, or unavailable Docker registry access. Do not delete volumes while diagnosing.

koakademy update creates a PostgreSQL dump before migrations. It does not silently roll back code after a migration because schema reversal can lose data. Inspect the release notes, task logs, and backup path printed by the command. Use koakademy rollback only after confirming the prior code supports the new schema; otherwise restore the database and image together.

Run the relevant configuration command again and check the app task after the rollout:

Terminal window
koakademy configure storage r2
koakademy configure mail smtp
koakademy configure search enable
docker service logs --tail=200 koakademy_app

Provider credentials are Docker Secrets and are intentionally not printable. Verify endpoint, bucket, allowed network access, and provider-side permissions instead of attempting to inspect secret values.

For the supported manual Compose topology:

Terminal window
docker compose --env-file .env -f compose.production.yaml config --quiet
docker compose --env-file .env -f compose.production.yaml ps
docker compose --env-file .env -f compose.production.yaml logs --tail=200 app
curl --verbose http://127.0.0.1:8000/up

Restart the app after a configuration change so FrankenPHP workers use the new environment.