Skip to main content

Updates and Scaling

Day-two operations: getting new releases in, and giving Kanera more room as the team grows.

Updates

Treat an update as a database change even when the release notes mention only application work. Record the running revision and take recoverable database and upload backups first.

Before updating

  1. Read the release notes for configuration changes or release-specific migration instructions.
  2. Confirm which revision is running and whether the checkout contains local changes:
git rev-parse HEAD
git status --short
  1. Back up PostgreSQL and uploaded files. Keep the database dump, upload snapshot, current .env, and current revision together outside the server's primary disk. See Storage and Backups.
  2. Confirm the current deployment is healthy. An update should not hide an existing database, storage, or routing problem.

If git status --short reports changes, review them before pulling. Keep deployment-specific Compose changes in an override file where possible; do not discard an operator's local changes to make the pull succeed.

Apply the update

From the repository root on the server:

git pull --ff-only
docker compose config --quiet
docker compose up -d --build

The dedicated migrate service runs pending database migrations once. The API, worker, and public API wait for it to complete successfully before starting, which prevents multiple API replicas from racing the same migration.

If the checkout cannot fast-forward, stop and reconcile its local commits or changes deliberately. Do not replace the checkout while its .env, override files, uploads, or backup credentials exist only there.

Verify the update

Check service state and the migration result before testing in a browser:

docker compose ps --all
docker compose logs migrate
docker compose logs --tail=100 api worker public-api mcp
curl https://kanera.example.com/api/health

Also check the public API and MCP health endpoints when you expose those services. Sign in, open a populated board, and verify one write operation such as creating and deleting a temporary card.

An update is complete only when the migration exited successfully, required services are healthy, and the app can read and write existing data.

If an update fails

  • If the migration did not start, fix the Compose or environment error and rerun docker compose up -d --build.
  • If the migration failed, keep the failed logs and database backup. Do not repeatedly rerun a migration without understanding the error.
  • Rebuilding an older application revision does not reverse database migrations. If a release cannot run safely against the migrated schema, stop the app, restore the pre-update database and uploads together, return the checkout to the recorded revision, and rebuild.
  • Never restore a backup over a running newer application. Test the restore procedure on a separate database or host before relying on it in production.

For Dokploy, redeploy the application after pushing or pulling the latest code. Dokploy rebuilds from docker-compose.yml.

Take a database backup before updating. See Storage and Backups.

Dokploy notes

Dokploy is a good fit for Kanera because it can run the Docker Compose application and terminate HTTPS through Traefik.

In Dokploy:

  1. Create a project and application.
  2. Choose Docker Compose.
  3. Connect the Kanera repository.
  4. Set the compose file path to docker-compose.yml.
  5. Add the production environment variables.
  6. Deploy once.
  7. Add domains for web, public-api, and mcp as needed.

Use these domain routes:

DomainServiceContainer port
kanera.example.comweb80
api.kanera.example.compublic-api3001
mcp.kanera.example.commcp3002

Do not create a public domain for the main api service.

Scaling notes

The default deployment uses multiple app API replicas and one worker.

  • API_REPLICAS controls app API process count. Defaults to 2.
  • PUBLIC_API_REPLICAS controls public API process count. Defaults to 1.
  • MCP_REPLICAS controls MCP process count. Defaults to 1.
  • Keep worker at exactly 1.
  • Valkey is required for realtime fanout, presence, and shared rate limits.

Raise replicas only when the server has enough CPU, memory, and database connection capacity. If you raise API_REPLICAS, review PG_POOL_MAX and PostgreSQL max_connections.

The single worker is deliberate. It runs schedulers, notification and webhook delivery, and cleanup jobs, and a second copy would duplicate that work rather than share it. If delivery is falling behind, look at database and outbound network capacity before considering more worker processes.