Skip to content
Guides

Deploy with a Dockerfile

Any language, any runtime. Kuppit builds your Dockerfile as written, caches its layers, and routes to port 8080.

A Dockerfile in the root directory is the build. Kuppit chooses it over automatic detection whenever one is present, builds it exactly as written with a layer cache kept per service, and starts the image with PORT set to 8080. This is how anything that is not a Node.js application runs on Kuppit, and how a Node.js application runs when you want control of the image.

What Kuppit detects

SettingValue
StrategyDockerfile, recommended whenever one exists.
Root directoryThe build context. COPY paths are relative to it.
Dockerfile pathDockerfile, relative to the root directory.
Port8080, always. The runtime sets PORT=8080 and routes there.

There is no framework, runtime, build or start command to set: the Dockerfile says all of it.

Writing the Dockerfile

Listen on PORT, on all interfaces. EXPOSE is documentation; the routing follows PORT.

Dockerfile
FROM node:24-slim AS build
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build

FROM node:24-slim
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app/.output ./.output
CMD ["node", ".output/server/index.mjs"]

Three things make a Dockerfile build well on Kuppit:

  • Order for the cache. Copy the manifest and lockfile, install, then copy the source. The install layer is reused across builds until the lockfile changes, which is most of the difference between a two-minute build and a ten-minute one.
  • Keep the context small. A .dockerignore with node_modules, .git and build output keeps the context Kuppit uploads to what the build needs.
  • No secrets in the image. Build arguments and ENV lines are baked into layers. Anything sensitive comes from the service's variables at runtime.

Variables reach the running container, not the build. A value the build itself needs must be in the repository.

Deploy

Follow the Quickstart: install the GitHub App, choose the repository and branch, confirm the proposal with Dockerfile as its strategy, and choose Deploy analyzed commit. The Build log shows each Dockerfile step; a failing step ends the deployment with The build failed.

Configuration and a database

The service's Variables tab is process.env, or its equivalent in your language, inside the container. A database is added the same way as for any service: Add resource, PostgreSQL, then a variable that is a Reference to databaseDATABASE_URL, and a redeploy. The value is a standard PostgreSQL connection string that any client library accepts.

From here

A build that exceeds 15 minutes fails with The build ran out of time; the cache ordering above is the usual fix. Build failures has the rest, and Builds the limits.

Was this page helpful?