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.

Django on single-node k3sOne VPSk3s installedTraefik ingresscert-manager TLSDjango podsDeployment + JobPostgresmanaged or backed upk3s gives Kubernetes workflows on one machine; the database still needs real backups.
  1. Install k3s on a VPS with 4 GB RAM: curl -sfL https://get.k3s.io | sh -.
  2. Install cert-manager and a Let's Encrypt ClusterIssuer.
  3. Deploy Django as a Deployment and Service, with an Ingress for Traefik that references the issuer.
  4. Use a managed PostgreSQL, or run PostgreSQL on the host or as a StatefulSet with scheduled pg_dump backups off the machine.
  5. Run migrations as a Job before each rollout.
  6. 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.

what k3s bundles and what you addTraefik ingress (bundled)routes example.com, TLScert-manager (added)Let's Encrypt certificatesDjango Deployment + Celeryrolling updateslocal-path storage (bundled)volumes on the node diskPostgreSQLmanaged, host, or StatefulSet +backupsBundled pieces make one VPS a working clusterdjango-deployment.com
local-path volumes live on one disk: if the node dies, so does that data.

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_dump and 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.

is single-node k3s the right tool?What do you need from the platform?one app, simple opsDocker Composefewer moving partsk8s workflows, low costSingle-node k3sHelm, GitOps, one VPShigh availabilityManaged Kubernetesmulti-node, managed control planeOne node is still one point of failuredjango-deployment.com
k3s can grow to multiple nodes later, but plan storage before you do.

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

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.

Keep going