Skip to main content

Build Methods

Kuploy supports multiple methods for building your application into a deployable container image. Choose the method that best fits your project.

Overview

MethodBest ForConfiguration
NixpacksMost applicationsZero config (auto-detect)
DockerfileCustom requirementsYou provide the Dockerfile
BuildpacksHeroku-like experienceMinimal config
StaticHTML/CSS/JS sitesZero config

Container Registry (required for builds)

Every build method except deploying a pre-built Docker image produces a container image that has to be pushed to a registry before Kuploy can run it on the cluster. There is no built-in/default registry — you provide one, and you attach it to each service.

This is a two-step setup, and missing the second step is the most common cause of a Registry required / Registry is required for Kubernetes builds deploy error:

  1. Add a registry to the platformSettings → Registry → Add Registry. Fill in:

    • Registry Name — any label (e.g. kuploy)
    • Username / Password — the registry credentials (for Harbor robot accounts this looks like robot$project+name)
    • Registry URL — the hostname only, no https:// and no path (e.g. registry.example.com)
    • Image Prefix — optional namespace/project images are pushed under (e.g. kuploy)

    Use Test Registry to verify the credentials before saving.

  2. Assign the registry to the service — open the application, go to the Advanced tab → Build Registry, select the registry you added, and Save.

Adding a registry is not enough — assign it per service

A registry added in Settings → Registry is only a credential the platform knows about. Builds push to the registry assigned on the service (Advanced → Build Registry). A service with no build registry assigned fails to deploy with "Registry required" even though a registry exists in Settings. Assign it on every Git-source application (each component of a Stack, too).

Editing a registry's password

For security the stored password is never sent back to the browser, so the Password field is blank when you edit an existing registry. Leave it blank to keep the current password; only type a value to change it (or to use Test Registry, which needs the live credentials).

Nixpacks automatically detects your language and framework, installs dependencies, and builds an optimized container image — with zero configuration for most projects.

Supported Languages

  • Node.js / JavaScript / TypeScript
  • Python
  • Go
  • Rust
  • Ruby
  • PHP
  • Java / Kotlin / Scala
  • .NET / C# / F#
  • Elixir
  • Haskell
  • Swift
  • Zig
  • And more

How It Works

  1. Nixpacks analyzes your repository
  2. Detects the language and framework (e.g., Next.js, Django, Rails)
  3. Generates an optimized build plan
  4. Builds and caches dependencies
  5. Produces a minimal container image

Customization

You can customize the build with a nixpacks.toml file:

[phases.setup]
nixPkgs = ["...", "ffmpeg"] # Add system packages

[phases.build]
cmds = ["npm run build"] # Custom build command

[start]
cmd = "npm start" # Custom start command

Or use environment variables:

NIXPACKS_BUILD_CMD=npm run build
NIXPACKS_START_CMD=npm start
NIXPACKS_PKGS=ffmpeg,imagemagick

When to Use

  • Standard web applications in any supported language
  • Projects that follow conventional directory structures
  • When you want the fastest path from code to container

Dockerfile

Use your own Dockerfile when you need full control over the build process.

Setup

  1. Select Dockerfile as the build method in your application settings
  2. Specify the Dockerfile path (default: Dockerfile)
  3. Optionally set the build context directory
  4. Optionally set a Docker Build Stage to target a specific stage in a multi-stage Dockerfile (empty = the last stage)
Deploying from a subdirectory (monorepo / examples repo)

When you set a Build Path in the Provider section (e.g. django-postgres), the Docker File and Docker Context Path are resolved relative to that build path. The Docker File field is required — for a Dockerfile at the root of the subdirectory, enter Dockerfile (not the full django-postgres/Dockerfile). You can leave Docker Context Path empty (it defaults to the Dockerfile's directory). This is how the kuploy/examples apps build.

Example: Node.js

FROM node:20-alpine

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .

EXPOSE 3000
CMD ["npm", "start"]

Example: Python

FROM python:3.12-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

EXPOSE 8000
CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:8000"]

Multi-Stage Builds

Use multi-stage builds to reduce image size:

# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Production stage
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/index.js"]

When to Use

  • You need specific system dependencies
  • You're using multi-stage builds
  • You need precise control over the container environment
  • Your project has an unconventional structure

Buildpacks

Cloud Native Buildpacks provide a Heroku-like build experience.

Setup

  1. Select Buildpacks as the build method
  2. Choose a buildpack provider:
    • Heroku — Most compatible with existing Heroku apps
    • Paketo — Modern, actively maintained
    • Google — GCP-oriented buildpacks

Procfile

Buildpacks use a Procfile to define the start command:

web: npm start
worker: node worker.js

When to Use

  • Migrating from Heroku
  • You're already using a Procfile-based workflow
  • You want automated builds without Nixpacks

Static Sites

For static HTML/CSS/JS sites, Kuploy serves files directly with no build step.

Select Static as the build method and specify:

  • Publish directory — The folder containing your static files (e.g., dist, build, public)

Framework Builds

For frameworks that generate static output (Vite, Next.js static export, Hugo, etc.), use Nixpacks or Dockerfile to build, then serve the output.

Build Tips

Keep Your Build Context Small

Your build context is the set of files sent to the build system. Smaller contexts mean faster builds and less build minute usage.

Use a .dockerignore file in your repository root to exclude files that aren't needed during the build:

# Dependencies (reinstalled during build)
node_modules/
vendor/
.venv/
__pycache__/

# Build output (regenerated during build)
.next/
dist/
build/
out/

# Development files
.git/
.env
.env.*
*.md
LICENSE
.vscode/
.idea/

# Test and CI
coverage/
.nyc_output/
__tests__/
*.test.*
*.spec.*
.github/
tip

Nixpacks builds automatically exclude common directories like node_modules, .next, dist, .cache, and vendor even without a .dockerignore. For Dockerfile builds, you should always create one.

warning

Builds with very large contexts (many large files, checked-in binaries, etc.) will be slower to start and may time out. If your repository contains large assets, exclude them via .dockerignore or store them externally (e.g., object storage).

Build Minutes

Build time counts against your plan's build minutes. To minimize build time:

  • Use .dockerignore to exclude unnecessary files (see above)
  • Leverage layer caching — put dependency install steps (npm ci, pip install) before copying application code
  • Use multi-stage builds to reduce final image size
  • Pre-built Docker images (using an existing image tag) don't consume build minutes

Build Environment Variables

Variables marked as Available at build time are injected during the build. See Environment Variables.

Troubleshooting Builds

SymptomLikely CauseFix
Build starts slowlyLarge build contextAdd .dockerignore to exclude node_modules, .git, etc.
Build times outComplex build or large image pushSimplify Dockerfile, use multi-stage builds, check your network
"Dockerfile not found"Wrong path configuredCheck Dockerfile Path in application settings
Build succeeds but app won't startWrong start command or portCheck CMD/ENTRYPOINT and port configuration
Nixpacks picks wrong languageAmbiguous project structureAdd a nixpacks.toml with explicit configuration