A production-ready K3s cluster on Oracle Cloud Infrastructure using Always Free tier resources. This project provisions infrastructure with Terraform, bootstraps Argo CD for GitOps, and deploys applications via Gateway API with automatic HTTPS.
graph TB
subgraph Internet
User((User))
CF[Cloudflare DNS]
end
subgraph OCI["Oracle Cloud (Always Free)"]
NLB[Network Load Balancer]
subgraph Public["Public Subnet"]
Ingress[k3s-ingress<br/>NAT + Envoy]
end
subgraph Private["Private Subnet"]
Server[k3s-server<br/>Control Plane]
Worker[k3s-worker<br/>Workloads]
end
end
subgraph GitHub
Repo[(Repository)]
end
User -->|HTTPS| CF
CF --> NLB
NLB --> Ingress
Ingress --> Server
Ingress --> Worker
Repo -->|GitOps| Server
The cluster runs on three Ampere A1 ARM64 instances within OCI's Always Free limits (4 OCPUs, 24GB RAM total):
| Node | Resources | Subnet | Role |
|---|---|---|---|
| k3s-ingress | 1 OCPU, 6GB | Public (10.0.1.0/24) | NAT gateway, Envoy Gateway |
| k3s-server | 2 OCPU, 12GB | Private (10.0.2.0/24) | K3s control plane, Argo CD |
| k3s-worker | 1 OCPU, 6GB | Private (10.0.2.0/24) | Application workloads |
| Component | Purpose |
|---|---|
| K3s | Lightweight Kubernetes distribution |
| Argo CD | GitOps continuous delivery |
| Envoy Gateway | Gateway API implementation |
| External DNS | Automatic Cloudflare DNS updates |
| Cert Manager | Let's Encrypt certificate automation |
| OCI Vault | Secrets storage (Always Free) |
| External Secrets | Sync Vault secrets to Kubernetes |
| Resource | Free Limit | Usage |
|---|---|---|
| Ampere A1 Compute | 4 OCPUs, 24 GB RAM | 4 OCPUs, 24 GB |
| Object Storage | 20 GB | ~1 MB (Terraform state) |
| Vault Secrets | 150 secrets | ~10 secrets |
| Vault Master Keys | 20 key versions | 1 key |
| Flexible NLB | 1 instance | 1 instance (ingress) |
flowchart LR
subgraph Infra["Infrastructure"]
TF[Terraform]
end
subgraph Cluster["K3s Cluster"]
Argo[Argo CD]
EG[Envoy Gateway]
CM[Cert Manager]
ED[External DNS]
end
subgraph External["External Services"]
OCI[(OCI)]
GH[(GitHub)]
GHCR[(GHCR)]
CFl[(Cloudflare)]
LE[(Let's Encrypt)]
Vault[(OCI Vault)]
end
TF -->|provisions| OCI
TF -->|provisions| Vault
TF -->|generates| GH
GH -->|Actions| GHCR
GH -->|syncs| Argo
GHCR -->|images| Argo
Argo -->|deploys| EG
Argo -->|deploys| CM
Argo -->|deploys| ED
Argo -->|applies| ES[ExternalSecret]
Vault -->|syncs to| ES
ES -->|creates| Secret[K8s Secret]
CM -->|certificates| LE
ED -->|DNS records| CFl
- OCI Account with Always Free eligibility
- Cloudflare account with a managed domain
- GitHub account with a Personal Access Token
- Terraform installed locally
Create tf-k3s/terraform.tfvars:
tenancy_ocid = "ocid1.tenancy.oc1..."
user_ocid = "ocid1.user.oc1..."
fingerprint = "xx:xx:xx..."
private_key_path = "/path/to/oci_api_key.pem"
region = "us-ashburn-1"
compartment_ocid = "ocid1.compartment.oc1..."
ssh_public_key_path = "/path/to/ssh_key.pub"
cloudflare_api_token = "your-cloudflare-token"
cloudflare_zone_id = "your-zone-id"
domain_name = "k3s.example.com"
acme_email = "admin@example.com"
git_repo_url = "https://github.com/your-user/k3s-oracle.git"
git_username = "your-github-username"
git_email = "your-email@example.com"
git_pat = "ghp_..."
k3s_token = "your-random-secure-token"
argocd_admin_password = "your-secure-password"cd tf-k3s
terraform init
terraform applyTerraform generates GitOps manifests that must be committed:
git add argocd/
git commit -m "Configure cluster manifests"
git pushWait approximately five minutes for bootstrapping, then verify:
ssh -J ubuntu@<ingress-ip> ubuntu@10.0.2.10 "sudo kubectl get applications -n argocd"GitHub Actions workflows handle linting and deployment:
| Workflow | Trigger | Purpose |
|---|---|---|
lint.yml |
Pull requests | Run pre-commit hooks (markdownlint, yamllint, tflint) |
docker-publish.yml |
Push to main (docs/) | Build Docker image, push to GHCR, restart docs pod |
For automatic docs deployment, add these secrets at Settings > Secrets > Actions:
| Secret | Value |
|---|---|
SSH_PRIVATE_KEY |
Contents of your SSH private key (same key used in ssh_public_key_path) |
INGRESS_IP |
Public IP of ingress node (from terraform output ingress_public_ip) |
pre-commit install
pre-commit run --all-filesThe full documentation is available at the live cluster site: https://k3s.sudhanva.me.
To view and edit the documentation locally:
cd docs
bun install
bun startMIT