API Deployment¶
The API runs on Node.js 24 in a Docker container. Build context is the repo root.
Historical note: the API previously ran on Cloudflare Workers. That architecture was retired — see
planning/migration-steps/for the migration record. This file now documents the current Docker-based deployment.
Architecture¶
┌─────────────────┐ ┌──────────────────┐ ┌─────────────┐
│ User Request │────▶│ Container Host │────▶│ Node.js │
└─────────────────┘ └──────────────────┘ └─────────────┘
│
┌──────────────────┐ │
│ PostgreSQL 18 │◀────────────────┘
└──────────────────┘
- Runtime: Node.js 24 (slim base image)
- Framework: Hono with OpenAPI
- Server:
@hono/node-server - Process: single Node process, multi-stage Docker build (
apps/api/Dockerfile) - Health check:
GET /health— verifies DB connectivity + required env vars
Prerequisites¶
- Docker (BuildKit-enabled)
- A Postgres 18 instance (local Docker via
compose.ymlfor dev, hoststack.dev for staging/prod)
Environments¶
| Environment | Host | URL |
|---|---|---|
| Production | hoststack.dev (managed PG) | https://api.meister-bill.com (TBD) |
| Staging | hoststack.dev (managed PG) | https://api-staging.meister-bill.com |
Configuration¶
Dockerfile: apps/api/Dockerfile
Multi-stage build:
1. builder — installs pnpm, builds schemas, builds API (prisma generate && tsc)
2. runtime — copies built artifacts, installs prod deps only, runs as non-root app user
Healthcheck hits /health every 30s.
Deployment¶
Build the image¶
docker build -f apps/api/Dockerfile -t meisterbill-api .
Run locally¶
docker run -d \
--name meisterbill-api \
-p 3001:3001 \
-e DATABASE_URL=postgresql://postgres:postgres@localhost:5432/meisterbill \
-e PORT=3001 \
-e APP_URL=http://localhost:3000 \
meisterbill-api
Required environment variables¶
See apps/api/.env.example for the full list. Required at runtime:
- DATABASE_URL — Postgres connection string
- PORT — listen port (default 3001)
- APP_URL — web origin (used for CORS + email redirects)
- GOTENBERG_URL — PDF rendering service
- Stripe keys (if Stripe features used)
- SENTRY_DSN (optional — Sentry disabled if unset)
CI/CD¶
.gitea/workflows/deploy-api.yml builds the image, smoke-tests /health, and is wired for a future registry push (TODO: registry credentials not yet configured).
Triggers: push to main with changes in apps/api/** or schemas/**.
Rollback¶
The image is tagged meisterbill-api:<git-sha>. To roll back, run the previous SHA:
docker stop meisterbill-api && docker rm meisterbill-api
docker run -d --name meisterbill-api ... meisterbill-api:<previous-sha>
If the schema has migrated forward incompatibly, restore the database from the most recent backup first.
Troubleshooting¶
Container exits immediately:
- Check DATABASE_URL is reachable from inside the container
- docker logs meisterbill-api for stack trace
/health returns 503:
- One or more required env vars missing — response body lists them
- DB unreachable — check DATABASE_URL + Postgres running
PDF generation fails:
- GOTENBERG_URL must point at a reachable gotenberg service (port 3003 in compose.yml)