ArgoCD on k3s VPS: GitOps Continuous Deployment for Kubernetes Without the Cloud Price Tag
ArgoCD is a declarative GitOps continuous delivery tool for Kubernetes — it watches a Git repository, compares the desired state (YAML manifests in Git) to the actual state (what’s running in Kubernetes), and automatically synchronizes them. On a VPS running k3s (lightweight Kubernetes), ArgoCD provides the same GitOps deployment experience used by large engineering teams — every deployment is a Git commit, rollbacks are git reverts, and the cluster always reflects what’s in your repository.
GitOps Principles
- Git is the single source of truth: All desired cluster state is in Git, not in imperative kubectl commands
- Declarative configuration: You describe desired state; ArgoCD reconciles the cluster to match
- Automatic drift detection: ArgoCD alerts (or auto-corrects) when cluster state diverges from Git
- Complete audit trail: Every deployment is a Git commit with author, message, and diff
Step 1: Install k3s
<code"># Install k3s — lightweight Kubernetes that runs on a single VPS curl -sfL https://get.k3s.io | sh - # Verify k3s is running sudo systemctl status k3s sudo kubectl get nodes # Should show your VPS as Ready # Set up kubectl without sudo mkdir -p ~/.kube sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config sudo chown $USER:$USER ~/.kube/config kubectl get nodes # Now works without sudo
Step 2: Install ArgoCD
<code"># Create argocd namespace and install
kubectl create namespace argocd
kubectl apply -n argocd \
-f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Wait for all pods to be ready
kubectl wait --for=condition=available deployment --all -n argocd --timeout=300s
# Check all pods are running
kubectl get pods -n argocd
Step 3: Expose ArgoCD UI
<code"># Method A: NodePort (simple, direct access)
kubectl patch svc argocd-server -n argocd \
-p '{"spec": {"type": "NodePort"}}'
# Get the NodePort
kubectl get svc argocd-server -n argocd
# Note the port (e.g., 30443) — access at https://YOUR_VPS_IP:30443
# Method B: Nginx reverse proxy (recommended for production)
# First, port-forward locally to set up, then use Nginx ingress:
kubectl port-forward svc/argocd-server -n argocd 8080:443 &
<code">sudo nano /etc/nginx/sites-available/argocd
<code">server {
listen 443 ssl http2;
server_name argocd.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/argocd.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/argocd.yourdomain.com/privkey.pem;
location / {
proxy_pass https://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_ssl_verify off; # ArgoCD uses self-signed cert internally
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 300s;
}
}
Step 4: Initial Login
<code"># Install argocd CLI
curl -sSL -o argocd \
https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
chmod +x argocd && sudo mv argocd /usr/local/bin/
# Get initial admin password
argocd admin initial-password -n argocd
# Login
argocd login argocd.yourdomain.com
# Username: admin
# Password: (from above command)
# Change password immediately
argocd account update-password
# Delete the initial secret
kubectl delete secret argocd-initial-admin-secret -n argocd
Step 5: Create an ArgoCD Application
Structure your Git repository:
<code"># myapp-gitops repository structure: # ├── apps/ # │ ├── mywebapp/ # │ │ ├── deployment.yaml # │ │ ├── service.yaml # │ │ └── ingress.yaml # │ └── database/ # │ ├── statefulset.yaml # │ └── service.yaml # └── README.md
<code"># apps/mywebapp/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mywebapp
namespace: production
spec:
replicas: 2
selector:
matchLabels:
app: mywebapp
template:
metadata:
labels:
app: mywebapp
spec:
containers:
- name: mywebapp
image: ghcr.io/myorg/mywebapp:latest
ports:
- containerPort: 3000
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
<code"># Create ArgoCD Application pointing to your Git repo
argocd app create mywebapp \
--repo https://github.com/myorg/myapp-gitops.git \
--path apps/mywebapp \
--dest-server https://kubernetes.default.svc \
--dest-namespace production \
--sync-policy automated \ # Auto-sync when Git changes
--auto-prune \ # Delete removed resources
--self-heal # Auto-correct manual changes
Step 6: Deploy via Git
<code"># To deploy a new version: # 1. Update image tag in deployment.yaml: # image: ghcr.io/myorg/mywebapp:v2.1.0 # 2. Commit and push to Git: git add apps/mywebapp/deployment.yaml git commit -m "deploy: bump mywebapp to v2.1.0" git push # 3. ArgoCD detects the change within 3 minutes (or immediately if webhook configured) # 4. Cluster is updated automatically # To rollback: git revert HEAD git push # ArgoCD applies the reverted state — rolls back the deployment
Step 7: Configure GitHub Webhook for Instant Sync
<code"># Instead of ArgoCD polling every 3 minutes, get instant sync:
# GitHub → Repository → Settings → Webhooks → Add webhook
# Payload URL: https://argocd.yourdomain.com/api/webhook
# Content type: application/json
# Secret: (generate and store in ArgoCD)
# Events: Push events
# Add webhook secret to ArgoCD:
kubectl create secret generic argocd-secret \
-n argocd \
--from-literal=webhook.github.secret=YOUR_WEBHOOK_SECRET \
--dry-run=client -o yaml | kubectl apply -f -
Monitor Deployments
<code"># CLI monitoring argocd app list # All applications and sync status argocd app get mywebapp # Detailed app status argocd app history mywebapp # Deployment history # UI: https://argocd.yourdomain.com # Shows: sync status, health status, resource tree, diff view, deployment history # Kubectl direct kubectl get pods -n production kubectl rollout status deployment/mywebapp -n production
Getting Started
k3s requires 1 GB RAM; ArgoCD adds another 200–400 MB. A 4 GB KVM VPS at VPS.DO runs k3s + ArgoCD + several application deployments comfortably. ArgoCD on k3s provides full GitOps infrastructure at VPS cost — the same deployment model used by teams running on EKS or GKE, but self-managed. For teams already using k3s, adding ArgoCD transforms ad-hoc kubectl deployments into an auditable, Git-driven workflow.
Conclusion
ArgoCD on k3s brings production-grade GitOps to a self-hosted VPS — every deployment is a Git commit, every rollback is a git revert, and the cluster continuously reconciles itself to match the repository state. Automated sync with self-heal means cluster drift (from manual kubectl commands) is automatically corrected. For development teams adopting Kubernetes, starting with k3s on a VPS plus ArgoCD provides the full GitOps experience before committing to managed Kubernetes costs.