Skip to main content

Docker

The backend ships as a multi-stage Docker image. See marketplace-fleapoai-service/Dockerfile.

Stages

StagePurpose
developmentFull dev deps + ts-node. Used for one-off CI tasks.
buildpnpm install --frozen-lockfile && pnpm build → emits dist/.
productionSlim runtime: copies dist/, node_modules/ (prod-only), drizzle/, docker-entrypoint.sh. Node 22-alpine base.

Three-stage keeps the final image small while reusing layer cache aggressively.

docker-entrypoint.sh

#!/bin/sh
set -e

node dist/src/db/migrate.js
node dist/seed.js
exec node dist/src/main.js

Runs on every container start. Migrations and seed are idempotent — re-running them is a no-op.

Build args

ArgUsed for
GIT_SHABaked into ENV; surfaced at GET /health
RELEASE_VERSIONBaked into ENV; surfaced at GET /health
docker build \
--build-arg GIT_SHA=$(git rev-parse HEAD) \
--build-arg RELEASE_VERSION=v1.2.3 \
-t marketplace:v1.2.3 .

Node version

node:22-alpine. Do not downgrade.

  • better-auth uses global crypto (Node 18+).
  • posthog-node likewise.

If you need a different distro (e.g. slim for native deps), update the FROM but keep the major version.

drizzle/ in production

The production stage copies drizzle/ (the SQL migration files + meta/_journal.json) so the migrator can resolve files at process.cwd()/drizzle at runtime. If you forget this COPY, the entrypoint will fail with Cannot find migrations folder.

ENV at runtime

ECS / Fargate / Kubernetes inject env vars into the container. The image itself ships with no production secrets. See Config & env vars.

Building for AWS Fargate

Apple Silicon developers must cross-build to amd64:

docker buildx build --platform linux/amd64 -t marketplace:v1.2.3 .

Otherwise you'll get exec format error on Fargate startup.

Local one-shot

docker build -t marketplace:dev .
docker run -p 3000:3000 --env-file .env marketplace:dev

Image size

Roughly 220 MB at time of writing. Most of that is node_modules/ from better-auth, drizzle-orm, posthog-node, @nestjs/*. Don't chase smaller without measuring — the migration runner needs everything.