ArgoCD on k3s VPS: GitOps Continuous Deployment for Kubernetes Without the Cloud Price Tag

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.

Fast • Reliable • Affordable VPS - DO It Now!

Get top VPS hosting with VPS.DO’s fast, low-cost plans. Try risk-free with our 7-day no-questions-asked refund and start today!