Kubernetes mode
portler up k8s runs the whole project in a local Kubernetes cluster. It targets local development clusters only: kind, minikube, and Docker Desktop Kubernetes.
portler up k8s # build, load, apply, port-forward
portler down k8s # delete everything, stop port-forwards
portler k8s render # only generate and print the manifestsWhat up k8s does
- Builds Docker images for services with a
buildconfig (same resolution asup docker: the service'sdocker:key wins over top-levelimage/build). - Detects the cluster from the active kubectl context (
kind-*,minikube,docker-desktop) and loads locally built images into it (kind load docker-image/minikube image load; Docker Desktop shares the host Docker daemon). No registry push is needed. Registry images likepostgres:16-alpineare pulled by the cluster itself. - Generates manifests into
.portler/k8s/: a Namespace, plus a Deployment, a Service (when the service has aport), and a PersistentVolumeClaim (whenk8s.volumeis set) per service. - Applies one dependency level at a time: a service's manifests are not applied until every dependency's pods have rolled out, stayed stable (ready with no new restarts for a few seconds), and answered through their port-forward — mirroring local and Docker startup ordering.
- Port-forwards each in-cluster Service to its Portler-assigned localhost port, so generated URLs and env values keep working exactly like local and Docker modes. The supervisor re-establishes the forward when a pod restart kills it (capped backoff, a few retries) and keeps running after
up k8s -dexits. Before reporting the stack up, Portler re-probes every forwarded port over TCP and fails loudly if one refuses connections.
Namespace
Everything lives in a project-scoped namespace (portler-<project>-<hash>, override with a top-level k8s_namespace:), so portler down k8s can delete it all and stop the port-forwards.
down k8s waits (up to 60s) for the namespace to finish terminating, and up k8s waits out a still-terminating namespace instead of failing.
Per-service k8s: options
services:
postgres:
image: postgres:16-alpine
port: 5432
env:
POSTGRES_USER: app
POSTGRES_PASSWORD: app
POSTGRES_DB: app
k8s:
volume:
size: 1Gi
mount_path: /var/lib/postgresql/data
api:
docker:
build:
context: .
dockerfile: apps/api/Dockerfile
port: 4000
port_env: PORT
depends_on:
- postgres
k8s:
replicas: 1
env:
DATABASE_URL: postgres://app:app@postgres:5432/app| Option | Meaning |
|---|---|
replicas | Deployment replica count (default 1) |
env | Env overrides for in-cluster consumption; layered over the service's Docker-mode env |
volume | Creates a PersistentVolumeClaim of the given size, mounted at mount_path |
env: inside the cluster, services reach each other through Kubernetes DNS service names (postgres:5432), just like Docker network aliases in Docker mode. Host-facing URLs (backend.url,PORTLER_*) keep using the port-forwarded localhost ports.volume:mount_pathdefaults to the container path of the first Docker volume, or/data. A plain string is shorthand for the size:volume: 1Gi.k8s: trueis shorthand for the defaults (replicas: 1, no overrides).
Ports and readiness
As with Docker services, port: is the internal container port; the in-cluster Service exposes it and Portler forwards a dynamic localhost port to it.
command healthchecks are ignored in Kubernetes mode (they usually shell into Docker containers); readiness uses the Deployment rollout plus a TCP check through the port-forward.
WARNING
k8s is a reserved word on the command line (portler up k8s) and cannot be used as a service name.