Build Methods
Kuploy supports multiple methods for building your application into a deployable container image. Choose the method that best fits your project.
Overview
| Method | Best For | Configuration |
|---|---|---|
| Nixpacks | Most applications | Zero config (auto-detect) |
| Dockerfile | Custom requirements | You provide the Dockerfile |
| Buildpacks | Heroku-like experience | Minimal config |
| Static | HTML/CSS/JS sites | Zero 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:
-
Add a registry to the platform — Settings → 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.
- Registry Name — any label (e.g.
-
Assign the registry to the service — open the application, go to the Advanced tab → Build Registry, select the registry you added, and Save.
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).
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 (Recommended)
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
- Nixpacks analyzes your repository
- Detects the language and framework (e.g., Next.js, Django, Rails)
- Generates an optimized build plan
- Builds and caches dependencies
- 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
- Select Dockerfile as the build method in your application settings
- Specify the Dockerfile path (default:
Dockerfile) - Optionally set the build context directory
- Optionally set a Docker Build Stage to target a specific stage in a multi-stage Dockerfile (empty = the last stage)
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 /app/dist ./dist
COPY /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
- Select Buildpacks as the build method
- 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/
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.
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
.dockerignoreto 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
| Symptom | Likely Cause | Fix |
|---|---|---|
| Build starts slowly | Large build context | Add .dockerignore to exclude node_modules, .git, etc. |
| Build times out | Complex build or large image push | Simplify Dockerfile, use multi-stage builds, check your network |
| "Dockerfile not found" | Wrong path configured | Check Dockerfile Path in application settings |
| Build succeeds but app won't start | Wrong start command or port | Check CMD/ENTRYPOINT and port configuration |
| Nixpacks picks wrong language | Ambiguous project structure | Add a nixpacks.toml with explicit configuration |