Back to blog
DevOps 14 min read

Minikube on a Dedicated VPS: Close Enough to Real Kubernetes

Run Minikube on a VPS, deploy a Hello World app, and configure SSL with cert-manager + Ingress for vault.by — a practice cluster that feels like production K8s.

MinikubeKubernetesVPSTLSIngress
Minikube on a Dedicated VPS: Close Enough to Real Kubernetes

Why Minikube on a dedicated VPS

Local Minikube on a laptop is fine for experiments, but it is still a lab box: no public IP, no real DNS/TLS muscle memory, no remote API habit. A cheap dedicated VPS with Minikube is a compromise — one control-plane/node, the same manifests, Ingress, cert-manager and kubectl flow as “real” Kubernetes, plus a domain such as vault.by.

This is not a replacement for managed Kubernetes (EKS/GKE/AKS) and not HA production. For side projects, demos and CI/CD practice the stack is already close: Deployment → Service → Ingress → Certificate.

Hardware baseline

  • VPS: from 2 vCPU / 4–8 GB RAM / 40+ GB SSD (the Docker driver is memory-hungry).
  • OS: Ubuntu 22.04/24.04 LTS.
  • Open ports: 22, 80, 443 (plus NodePort range if needed).
  • DNS: A record for vault.by (and preferably www.vault.by) → VPS public IP.

Install Minikube and start

sudo apt update && sudo apt install -y docker.io curl
sudo usermod -aG docker $USER
# re-login, then:
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube

minikube start --driver=docker --cpus=2 --memory=3900 --addons=ingress
minikube addons enable ingress
minikube addons enable metrics-server
kubectl get nodes

To expose Ingress from the internet on a Docker-driver Minikube you usually forward host ports to the ingress controller (or keep minikube tunnel running). A practical single-VPS option:

kubectl -n ingress-nginx get svc

sudo iptables -t nat -A PREROUTING -p tcp --dport 80 \
  -j DNAT --to-destination $(minikube ip):80
sudo iptables -t nat -A PREROUTING -p tcp --dport 443 \
  -j DNAT --to-destination $(minikube ip):443
# plus MASQUERADE for replies; or nginx on the host → $(minikube ip)

Often simpler to operate: nginx/Caddy on the host as a reverse proxy to the Minikube Ingress IP, while 80/443 listen only on the host.

Hello World: Deployment + Service

# hello-world.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-world
  labels:
    app: hello-world
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hello-world
  template:
    metadata:
      labels:
        app: hello-world
    spec:
      containers:
        - name: web
          image: hashicorp/http-echo:1.0
          args: ["-text=Hello from Minikube on VPS"]
          ports:
            - containerPort: 5678
          resources:
            requests:
              cpu: 50m
              memory: 64Mi
            limits:
              cpu: 200m
              memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
  name: hello-world
spec:
  selector:
    app: hello-world
  ports:
    - port: 80
      targetPort: 5678
kubectl apply -f hello-world.yaml
kubectl get pods,svc
kubectl port-forward svc/hello-world 8080:80
# curl http://127.0.0.1:8080

Ingress for vault.by

# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello-world
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - vault.by
        - www.vault.by
      secretName: vault-by-tls
  rules:
    - host: vault.by
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: hello-world
                port:
                  number: 80
    - host: www.vault.by
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: hello-world
                port:
                  number: 80

SSL with cert-manager (Let's Encrypt)

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.2/cert-manager.yaml
kubectl -n cert-manager rollout status deploy/cert-manager
# cluster-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: admin@vault.by
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
      - http01:
          ingress:
            class: nginx
kubectl apply -f cluster-issuer.yaml
kubectl apply -f ingress.yaml
kubectl describe certificate vault-by-tls
kubectl get secret vault-by-tls
curl -I https://vault.by

If DNS does not point at the VPS or port 80 is unreachable from the internet, the HTTP-01 challenge fails and the certificate stays Pending/False. Verify curl http://vault.by first, then TLS.

What feels production-like — and what does not

  • Similar: manifests, probes (add them), Ingress, TLS, namespaces, ResourceQuota, GitOps-style apply from CI.
  • Not prod: single node, no HA etcd/API, weaker isolation, Minikube addons, VPS uptime = cluster uptime.
  • Next step: k3s/k0s on the same VPS or managed K8s when you need multiple nodes and an SLA.

Checklist before a vault.by demo

  1. Domain A record → VPS IP.
  2. minikube start + ingress addon.
  3. Forward 80/443 to Ingress.
  4. Hello World Deployment/Service.
  5. cert-manager + ClusterIssuer + TLS Ingress.
  6. curl -I https://vault.by → 200 and a valid certificate.

Takeaway

Minikube on a dedicated VPS is a training cluster that already feels grown-up: the same Kubernetes objects, a public domain, and real Let's Encrypt. For Hello World on vault.by you only need Deployment, Service, Ingress and cert-manager. It will not replace a production cluster — but it is the best bridge from a laptop sandbox to real K8s practice.

Alexey Prybytkou

Senior Software Developer

© 2026 All rights reserved