Deploy Django on a Single-Node k3s Cluster
Run Django on a single-node k3s Kubernetes cluster: install k3s, use the bundled Traefik ingress with cert-manager for Let's Encrypt, keep PostgreSQL outside the cluster or back it up properly, and deploy with manifests or Helm.
Problem statement
You want Kubernetes workflows for a Django app (declarative manifests, rolling updates, Helm, GitOps) without the cost of a managed cluster. k3s is a lightweight, certified Kubernetes distribution that runs on a single small VPS or a home server. It bundles an ingress controller (Traefik), a local storage provisioner, and a service load balancer, so a single machine becomes a working cluster in a minute. The risks are the ones any single server has, plus Kubernetes' complexity, so the setup needs a clear plan for the database and backups.
- Install k3s on a VPS with 4 GB RAM:
curl -sfL https://get.k3s.io | sh -. - Install cert-manager and a Let's Encrypt
ClusterIssuer. - Deploy Django as a Deployment and Service, with an Ingress for Traefik that references the issuer.
- Use a managed PostgreSQL, or run PostgreSQL on the host or as a StatefulSet with scheduled
pg_dumpbackups off the machine. - Run migrations as a Job before each rollout.
- Back up the k3s state (
/var/lib/rancher/k3s/server) and keep manifests in Git.
Step-by-step solution
1. Install k3s
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--write-kubeconfig-mode 600" sh -
sudo k3s kubectl get nodes
Copy /etc/rancher/k3s/k3s.yaml to your workstation (replacing 127.0.0.1 with the server address, reachable over a VPN or SSH tunnel) to use kubectl remotely. Do not expose the API port 6443 to the internet; restrict it in the firewall.
2. Certificates with cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.2/cert-manager.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata: { name: letsencrypt }
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: ops@example.com
privateKeySecretRef: { name: letsencrypt-account }
solvers: [{ http01: { ingress: { ingressClassName: traefik } } }]
3. Deploy Django
apiVersion: apps/v1
kind: Deployment
metadata: { name: web }
spec:
replicas: 2
selector: { matchLabels: { app: web } }
template:
metadata: { labels: { app: web } }
spec:
containers:
- name: web
image: ghcr.io/you/myapp:SHA
ports: [{ containerPort: 8000 }]
envFrom: [{ secretRef: { name: django-env } }]
readinessProbe: { httpGet: { path: /health/, port: 8000, httpHeaders: [{ name: Host, value: example.com }] } }
resources: { requests: { cpu: 200m, memory: 300Mi }, limits: { memory: 600Mi } }
---
apiVersion: v1
kind: Service
metadata: { name: web }
spec: { selector: { app: web }, ports: [{ port: 80, targetPort: 8000 }] }
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
annotations: { cert-manager.io/cluster-issuer: letsencrypt }
spec:
ingressClassName: traefik
tls: [{ hosts: [example.com], secretName: web-tls }]
rules: [{ host: example.com, http: { paths: [{ path: /, pathType: Prefix, backend: { service: { name: web, port: { number: 80 } } } }] } }]
Two replicas on one node still help: rolling updates replace one pod at a time, so deploys do not drop traffic. Traefik terminates TLS, so set SECURE_PROXY_SSL_HEADER in Django.
4. Decide where PostgreSQL lives
On a single node, every volume is a directory on one disk. Options, from safest to cheapest:
- Managed PostgreSQL at your provider: backups and upgrades handled, app in k3s.
- PostgreSQL on the host (apt package), with standard backups; pods connect to the node IP.
- PostgreSQL StatefulSet with a local-path PVC and a CronJob that runs
pg_dumpand uploads to object storage.
Whichever you pick, test restores.
5. Migrations and Celery
Run python manage.py migrate as a Job with the same image and secrets before updating the Deployment image. Celery workers run as another Deployment; Beat as a Deployment with one replica and strategy: Recreate so two never overlap during rollouts.
6. Back up the cluster itself
Keep every manifest or Helm values file in Git so the cluster can be rebuilt. k3s keeps its state in an embedded SQLite (or etcd) database under /var/lib/rancher/k3s/server; k3s etcd-snapshot works with embedded etcd. Secrets created by hand are the easiest thing to lose; store them encrypted (SOPS or Sealed Secrets) in Git or in an external secret store.
Verification check: kubectl get pods,ingress,certificate shows pods Ready and the certificate True, https://example.com serves a Let's Encrypt certificate, a rollout with a new image completes without failed requests, and a restore from the database backup works.
Explanation
k3s packages Kubernetes as a single binary with lightweight defaults: SQLite instead of etcd for single servers, Traefik as ingress, a simple load balancer, and local-path storage. It runs the same APIs as any cluster, so Django manifests, Helm charts, and GitOps tools work unchanged and transfer to managed Kubernetes later. On one node, Kubernetes adds rolling deploys and declarative configuration but not redundancy, so data durability depends entirely on backups that leave the machine.
Edge cases / notes
- Memory. k3s itself uses around 500 MB; with PostgreSQL, Redis, Django, and Celery, 4 GB is a comfortable minimum.
- Firewall. Allow 80 and 443; restrict 6443 (API) and 10250 (kubelet) to trusted addresses.
- Upgrades. Use the system-upgrade-controller or re-run the installer with a pinned version; read release notes.
- Traefik CRDs (IngressRoute) offer more features, but standard Ingress keeps manifests portable.
Next steps
- Package the app with Deploy Django With a Helm Chart.
- Simpler multi-container alternative: Deploy Django With Docker Swarm.
FAQ
Is k3s production-ready?
Yes, k3s is a certified Kubernetes distribution. A single node is not highly available, so back up data carefully.
Do I need an ingress controller with k3s?
k3s includes Traefik by default. Add cert-manager for automatic Let's Encrypt certificates.
Should PostgreSQL run inside k3s?
It can, with a persistent volume and off-machine backups. A managed database or host install is simpler to operate.
How much RAM does k3s need for Django?
About 4 GB for k3s, PostgreSQL, Redis, Django, and Celery on one node.