Docker and Build
Cashlytics and the documentation site are built as separate container images.
App Image
- Repository:
ghcr.io/aaronjoeldev/cashlytics-ai - Tags for releases:
vX.Y.Z,latest, and the short commit SHA (7 characters) - Tags for the
devbranch:dev-X.Y.Z,dev-latest,dev-<short-sha> - Runtime: Next.js standalone output, listening on port
3000 - Purpose: application runtime
The release workflow (release.yml in the app repository) builds and pushes these tags. Pushes to main that do not produce a release do not overwrite the published tags.
Dockerfile stages
| Stage | Purpose |
|---|---|
deps | Installs dependencies with npm ci |
builder | Runs npm run build |
migrate-deps | Installs drizzle-kit, drizzle-orm, and postgres into /migrate so the runtime image stays small |
runner | node:24-alpine, runs as the non-root user nextjs (uid 1001), sets PORT=3000 and HOSTNAME=0.0.0.0, exposes port 3000 |
Startup and migrations
The container runs /entrypoint.sh. It applies database migrations with drizzle-kit migrate using DATABASE_URL from the runtime environment. If a migration attempt fails (for example because PostgreSQL is not ready yet), it retries up to 5 times, 3 seconds apart. If all attempts fail, the container exits with code 1 and does not start the app, so the restart policy can try again. After a successful migration it starts node /app/server.js.
Build-time variables
NEXT_PUBLIC_* variables are compiled into the client bundle during npm run build. The Dockerfile has no build arguments and .dockerignore excludes .env, so setting them at runtime has no effect on the published image. To change NEXT_PUBLIC_DEFAULT_LOCALE or NEXT_PUBLIC_DEFAULT_CURRENCY, build your own image and set them in the builder stage. See Environment Variables.
Build the app locally
docker compose up -d --build
This uses docker-compose.yml in the app repository: the image is built from the Dockerfile, the app is available on port 3000, and PostgreSQL is exposed on port 5432 with the fixed password cashlytics_secret. This file is meant for local use only. Unlike docker-compose.selfhost.yml, it does not pass AUTH_TRUST_HOST or EMAIL_TRANSPORT to the container.
Docs Image
- Repository:
ghcr.io/<owner>/cashlytics-docs - Tags:
latest(default branch) andsha-<commit> - Runtime: static build served via Nginx on port 80
- Purpose: user and developer documentation at docs domain
Build Commands
Run these from the root of the cashlytics-lp repository.
Build docs locally
cd docs-site
npm install
npm run build
Build docs container locally
docker build -f docs-site/Dockerfile -t cashlytics-docs:local docs-site
CI Scope
- Pull requests that touch
docs-site/build the docs image without pushing it. - Pushes to
mainbuild the docs image and push it to GHCR (latestandsha-<commit>). - CI does not deploy anything. Routing and release on the VPS are handled manually.
- A separate workflow (
docker.yml) builds the landing page image on pushes tomainandv*tags.
Related Guides
- For production app hosting, see Self-Hosting.
- For complete configuration, see Environment Variables.