Ship Conduit with its bundled Dockerfile
The three stages#
deps installs dependencies with bun install --frozen-lockfile against package.json and bun.lock, cached separately from the rest of the build so dependency installs aren't repeated on every code change.
builder copies the installed node_modules, brings in the full source, runs bun run prisma generate, then bun run build. Every environment variable from Environment Variables gets an ENV line here with a ${VAR:-default} fallback — real secrets aren't required for the build to succeed, only for the app to work once it's running.
runner is the image that actually ships. It runs as the non-root bun user (UID/GID 1000) that the base image provides, listens on port 3000, and starts with:
bun run prisma migrate deploy && bun run startEvery deploy applies pending Prisma migrations before the server starts — you don't run migrations as a separate step in production.
Written for Coolify#
The Dockerfile's own comments call out that it's built around how Coolify injects build arguments — as ARG KEY=value lines appended right after each FROM. The comments warn against redeclaring those same ARGs inside a stage, since a later no-value ARG KEY would shadow the injected value and silently reset it to empty. If you're adapting this Dockerfile for a different platform, keep that ordering constraint in mind.
What to configure at the platform level#
Set the variables from Environment Variables as runtime environment variables on whatever platform you deploy to. DATABASE_URL needs to point at a reachable Postgres instance before the container's prisma migrate deploy step can succeed.
Related: Environment Variables for what each variable does, Architecture for how the scan service fits alongside this container, or back to the documentation overview.