GitOps has revolutionized how teams deploy and manage applications in Kubernetes environments, providing declarative configuration management with Git as the single source of truth. When combined with a lightweight k3s cluster, Flux v2, encrypted secrets management through SOPS/age, and modern OCI artifacts, you get a powerful, production-ready deployment pipeline.
This comprehensive tutorial walks through setting up a complete GitOps workflow on an Ubuntu 24.04 VPS, leveraging Flux v2’s advanced features for automated deployments and secure secrets handling.
Prerequisites
Before starting, ensure you have:
- Ubuntu 24.04 LTS VPS with at least 4GB RAM and 2 vCPUs
- Root or sudo access to your server
- A working k3s cluster deployment
- Git repository for storing manifests
- Basic knowledge of Kubernetes and Git workflows
Note: This tutorial assumes you have a functioning k3s cluster. If you need to set one up first, follow our detailed k3s deployment guide.
Step 1: Install Flux CLI
First, install the Flux CLI on your Ubuntu 24.04 system:
# Download and install Flux CLI v2.2.3
curl -s https://fluxcd.io/install.sh | sudo bash
# Verify installation
flux --version
# Check cluster prerequisites
flux check --pre
The flux check --pre command ensures your k3s cluster meets all requirements for Flux installation.
Step 2: Bootstrap Flux with OCI Support
Bootstrap Flux v2 in your cluster with OCI artifact support enabled:
# Set your Git repository details
export GITHUB_TOKEN=your_github_token
export GITHUB_USER=your_username
export GITHUB_REPO=your-gitops-repo
# Bootstrap Flux with OCI support
flux bootstrap github \
--owner=$GITHUB_USER \
--repository=$GITHUB_REPO \
--branch=main \
--path=./clusters/production \
--components-extra=image-reflector-controller,image-automation-controller
This command creates the necessary Flux components and configures your Git repository as the source of truth for your GitOps workflow.
Step 3: Configure SOPS/age Encryption
Install and configure age for encrypting sensitive data:
# Install age
sudo apt update
sudo apt install age -y
# Generate age key pair
age-keygen -o age.agekey
# Display public key (save this for SOPS configuration)
grep 'public key:' age.agekey
Create a SOPS configuration file in your Git repository:
# .sops.yaml
creation_rules:
- path_regex: \.secrets\.ya?ml$
age: age1youragepublickeyhere
- path_regex: secrets/.*\.ya?ml$
age: age1youragepublickeyhere
Install SOPS for secret encryption:
# Install SOPS v3.8.1
wget https://github.com/mozilla/sops/releases/download/v3.8.1/sops-v3.8.1.linux.amd64
sudo mv sops-v3.8.1.linux.amd64 /usr/local/bin/sops
sudo chmod +x /usr/local/bin/sops
Step 4: Create Encrypted Secrets
Create a sample encrypted secret:
# Create a plain secret file
cat > app-secrets.yaml << EOF
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: default
data:
database-url: cG9zdGdyZXNxbDovL3VzZXI6cGFzc0BkYi5leGFtcGxlLmNvbS9teWRi
api-key: bXlfc3VwZXJfc2VjcmV0X2FwaV9rZXk=
EOF
# Encrypt the secret with SOPS
SOPS_AGE_KEY_FILE=age.agekey sops --encrypt --in-place app-secrets.yaml
Step 5: Configure Flux Decryption
Create a Kubernetes secret containing your age private key:
# Create age secret in flux-system namespace
cat age.agekey |
kubectl create secret generic sops-age \
--namespace=flux-system \
--from-file=age.agekey=/dev/stdin
Configure Flux to use SOPS for decryption by creating a Kustomization:
# clusters/production/apps-kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: apps
namespace: flux-system
spec:
interval: 10m0s
path: ./apps
prune: true
sourceRef:
kind: GitRepository
name: flux-system
decryption:
provider: sops
secretRef:
name: sops-age
Step 6: Set Up OCI Artifact Source
Configure an OCI repository as a source for your applications:
# clusters/production/oci-source.yaml
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: OCIRepository
metadata:
name: webapp-oci
namespace: flux-system
spec:
interval: 5m
url: oci://ghcr.io/yourorg/webapp-config
ref:
tag: latest
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: webapp
namespace: flux-system
spec:
interval: 10m
sourceRef:
kind: OCIRepository
name: webapp-oci
path: "./"
prune: true
Step 7: Implement Automated Image Updates
Configure automated image updates using Flux’s image automation:
# clusters/production/image-repository.yaml
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
name: webapp
namespace: flux-system
spec:
image: ghcr.io/yourorg/webapp
interval: 5m
---
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
name: webapp
namespace: flux-system
spec:
imageRepositoryRef:
name: webapp
policy:
semver:
range: '>=1.0.0'
---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
name: webapp
namespace: flux-system
spec:
interval: 30m
sourceRef:
kind: GitRepository
name: flux-system
git:
checkout:
ref:
branch: main
commit:
author:
email: [email protected]
name: Flux
messageTemplate: |
Automated image update
Automation name: {{ .AutomationObject }}
Files:
{{ range $filename, $_ := .Updated.Files -}}
- {{ $filename }}
{{ end -}}
Objects:
{{ range $resource, $_ := .Updated.Objects -}}
- {{ $resource.Kind }} {{ $resource.Name }}
{{ end -}}
push:
branch: main
update:
path: "./apps"
strategy: Setters
Step 8: Deploy Sample Application
Create a sample application deployment with image automation markers:
# apps/webapp/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
containers:
- name: webapp
image: ghcr.io/yourorg/webapp:1.0.0 # {"$imagepolicy": "flux-system:webapp"}
ports:
- containerPort: 8080
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: database-url
Best Practices
Follow these GitOps security practices for production deployments:
- Separate environments: Use different Git branches or repositories for staging and production
- RBAC implementation: Configure appropriate role-based access controls for Flux service accounts
- Monitoring setup: Implement comprehensive monitoring with tools like our observability stack guide
- Backup strategy: Regularly backup your Git repositories and implement automated VPS backups
- Network security: Consider using Tailscale for secure cluster access
Security Warning: Never commit unencrypted secrets to Git repositories. Always use SOPS encryption for sensitive data and rotate age keys regularly.
Conclusion
You’ve successfully implemented a production-ready GitOps pipeline with Flux v2, encrypted secrets management, and OCI artifact support on your k3s cluster. This setup provides automated deployments, secure secrets handling, and modern container registry integration.
The combination of Flux v2’s advanced features with SOPS encryption and OCI artifacts creates a robust, scalable deployment platform. Your applications will now automatically update when new images are available, while maintaining security through encrypted secrets and version-controlled infrastructure.
Ready to deploy more advanced workloads? Explore our guides on RAG stack deployment or consider upgrading to high-performance Onidel VPS in Singapore with EPYC processors for demanding Kubernetes workloads.




