Skip to main content
Version: 0.5.4

k3s Single Node on Aruba Cloud

Deploy a production-ready k3s single-node Kubernetes cluster on Aruba Cloud using Terraform and cloud-init. Includes the built-in Traefik ingress controller — no manual configuration required.

Provider version: arubacloud/arubacloud ~> 0.5 | Terraform: ≥ 1.9


Introduction​

k3s is a lightweight, CNCF-certified Kubernetes distribution packaged as a single binary. It is ideal for edge computing, homelabs, CI environments, and small production workloads. This example provisions a single-node k3s cluster on Aruba Cloud with:

  • A CloudServer VM (CSO4A8 — 4 vCPU / 8 GB) running k3s, fully bootstrapped by cloud-init
  • The built-in Traefik v2 ingress controller for routing HTTP/HTTPS traffic to services
  • A dedicated VPC, subnet, and security group via the shared network module
  • An Elastic IP pinned to the node for stable external access
  • The Kubernetes API server pre-configured with the node's Elastic IP as a TLS SAN — so kubectl works immediately from your laptop without certificate errors
  • A ready-to-use kubeconfig placed in ~ubuntu/.kube/config on the node after boot

Architecture Overview​

k3s runs as a single systemd service combining the control plane and worker node. Traefik is installed by k3s as a DaemonSet and handles all Ingress resources.


Infrastructure Created​

ResourceName patternDescription
arubacloud_projectk3s-prodProject container
arubacloud_vpck3s-prod-vpcVirtual Private Cloud
arubacloud_subnetk3s-prod-subnetBasic subnet
arubacloud_securitygroupk3s-prod-vm-sgSecurity group
arubacloud_securityrulek3s-prod-vm-sshSSH ingress (restricted CIDR)
arubacloud_securityrulek3s-prod-vm-httpHTTP ingress (0.0.0.0/0)
arubacloud_securityrulek3s-prod-vm-httpsHTTPS ingress (0.0.0.0/0)
arubacloud_securityrulek3s-prod-vm-k8s-apiKubernetes API ingress (restricted CIDR)
arubacloud_elasticipk3s-prod-vm-eipNode public IP
arubacloud_blockstoragek3s-prod-boot80 GB boot disk (Performance)
arubacloud_keypairk3s-prod-keypairSSH public key
arubacloud_cloudserverk3s-prod-vmCloudServer VM

VM Sizing Recommendation​

WorkloadvCPURAMDiskFlavor
Dev / CI / low-traffic48 GB80 GBCSO4A8 (default)
Medium production816 GB100 GBCSO8A16

A single-node cluster cannot reschedule pods on failure. For high availability, consider a multi-node setup (see the k3s HA example in Phase 3).


Estimated Monthly Cost​

Approximate prices for ITBG-Bergamo, hourly billing. Actual prices may vary.

ResourceSpecEst. cost/mo
CloudServer VMCSO4A8 — 4 vCPU / 8 GB~€36
Boot disk80 GB Performance~€10
Elastic IP—~€3
Total~€49/mo

Requirements​

  • Terraform ≥ 1.9
  • ArubaCloud Terraform Provider ~> 0.5
  • An ArubaCloud account with OAuth2 API credentials
  • An SSH key pair
  • kubectl installed locally to interact with the cluster

Variables​

Required​

VariableDescription
arubacloud_client_idArubaCloud OAuth2 client ID
arubacloud_client_secretArubaCloud OAuth2 client secret
ssh_public_keySSH public key content

Optional​

VariableDefaultDescription
app_name"k3s"Short name used in all resource names
environment"prod"Environment label
location"ITBG-Bergamo"ArubaCloud region
zone"ITBG-1"Availability zone
billing_period"Hour""Hour" or "Month"
vm_flavor"CSO4A8"CloudServer flavor
vm_image"LU22-001"Boot disk image (Ubuntu 22.04 LTS)
vm_disk_size_gb80Boot disk size in GB
ssh_cidr"0.0.0.0/0"CIDR for SSH — restrict to your IP in production
api_cidr"0.0.0.0/0"CIDR for Kubernetes API (port 6443) — restrict to your IP in production
k3s_version"v1.32.3+k3s1"k3s release — check github.com/k3s-io/k3s/releases
cluster_domain""Optional DNS name added as TLS SAN to the API server certificate

Outputs​

OutputDescription
vm_public_ipPublic IP address of the k3s node
api_endpointKubernetes API server URL
ssh_commandSSH command to connect to the node
kubeconfig_cmdOne-liner to fetch the kubeconfig to your local machine

Deployment Instructions​

1. Clone and navigate​

git clone https://github.com/arubacloud/terraform-arubacloud-examples.git
cd terraform-arubacloud-examples/k3s-single

2. Configure variables​

cp terraform.tfvars.example terraform.tfvars

Edit terraform.tfvars with your credentials and SSH key.

3. Initialize and deploy​

terraform init
terraform plan
terraform apply

Bootstrap takes approximately 3–5 minutes — k3s installs fast since it is a single binary.

4. Fetch kubeconfig​

# Copy the one-liner from outputs:
terraform output -raw kubeconfig_cmd | bash

# Or manually:
ssh ubuntu@$(terraform output -raw vm_public_ip) \
'cat ~/.kube/config' > ~/.kube/k3s-arubacloud.yaml
export KUBECONFIG=~/.kube/k3s-arubacloud.yaml

5. Verify the cluster​

kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# k3s-prod-vm Ready control-plane,master 2m v1.32.3+k3s1

kubectl get pods -A
# Traefik, CoreDNS, and metrics-server should all be Running.

6. Deploy a workload​

kubectl create deployment hello --image=nginx --replicas=1
kubectl expose deployment hello --port=80 --type=ClusterIP

# Create an Ingress to route external traffic:
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello
port:
number: 80
EOF

curl http://$(terraform output -raw vm_public_ip)

Destroy Instructions​

terraform destroy

All cloud resources are deleted. Any data stored in PersistentVolumes on the boot disk is lost.


Security Recommendations​

  1. Restrict api_cidr to your IP. Exposing port 6443 to 0.0.0.0/0 allows anyone to attempt authentication against the API. Set api_cidr = "your.ip/32".

  2. Restrict ssh_cidr to your IP. Set ssh_cidr = "your.ip/32".

  3. Rotate the node token periodically. The k3s node token lives at /var/lib/rancher/k3s/server/node-token. Keep it secret.

  4. Use RBAC for workloads. k3s ships with RBAC enabled. Create service accounts with least-privilege roles for each application.

  5. Enable TLS for Ingress. Use cert-manager with Let's Encrypt to provision certificates for your Ingress resources, or configure Traefik's built-in ACME support.


Upgrade Considerations​

k3s upgrade​

k3s can be upgraded in-place using the same install script with a new version:

ssh ubuntu@$(terraform output -raw vm_public_ip)
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION="vX.Y.Z+k3s1" sh -

For zero-downtime upgrades in production, use the System Upgrade Controller.

Changing the node flavor​

Changing vm_flavor or vm_disk_size_gb in terraform.tfvars and running terraform apply will replace the VM. Back up all PersistentVolume data first.


Troubleshooting​

k3s is not running after apply​

ssh ubuntu@$(terraform output -raw vm_public_ip)
sudo systemctl status k3s
sudo journalctl -u k3s -n 100
sudo tail -f /var/log/cloud-init-output.log

kubectl returns certificate error​

The API server certificate includes the node's Elastic IP as a SAN. If you set cluster_domain, ensure that DNS resolves to the Elastic IP and use the domain in your kubeconfig server URL.

Pods stuck in Pending​

Check node resources:

kubectl describe node
kubectl get events --sort-by='.lastTimestamp'

Single-node clusters run both control-plane and workloads on the same VM — ensure the flavor has enough memory for your workloads.


References​