OpenTofu infrastructure for OCI Always Free tier - provisions Ampere A1.Flex
(ARM64) by default, with E2.1.Micro instances opt-in via micro_nodes.
Supports two modes via the omni_ready toggle:
| Mode | omni_ready |
OS | Kubernetes |
|---|---|---|---|
| Default | false |
Ubuntu (custom image) | Bring your own |
| Talos + Omni | true |
Talos Linux | Enrolled into Omni via SideroLink |
tofu/oci/ OpenTofu module — instances, networking, budget
scripts/ Helper scripts (state backend, capacity check)
- OCI account (PAYG recommended for Ampere availability)
- OCI CLI configured (
~/.oci/config) - OpenTofu ≥ 1.8
cp tofu/oci/terraform.tfvars.example tofu/oci/terraform.tfvars
# Edit terraform.tfvars — set compartment OCID, SSH key, image OCIDsKey variables:
# Default (Ubuntu)
omni_ready = false
# Talos + Omni enrollment
omni_ready = true
talos_image_ocid = "ocid1.image.oc1..." # explicit immutable custom-image identity
omni_endpoint = "omni.example.com:8090"
omni_join_token = "..." # or pass via -var / TF_VAR_omni_join_token
# Optional K8s ingress reserved IP (default: disabled)
create_ingress_ip = truecd tofu/oci
tofu init
tofu plan
tofu apply.github/workflows/release-dispatch.yml accepts only a complete, controller-originated
talos-release-request. It binds the approved source SHA, Talos version, build run,
OCI image OCID, installer reference, and manifest digest before it can dispatch
deploy.yml. The request must carry an explicit oci_scope whose targets are only
the two oci_core_instance.ampere_instance Talos addresses; Micro instances,
networking, and empty scopes are rejected. Replacements are disabled unless the
approved scope names the same exact Talos target.
The receiver reserves each release_id as a GitHub Deployment before dispatching,
so a duplicate delivery is reported as failure rather than repeating a replacement.
Coordinator deploys still use the existing plan/destructive-change gates and add a
live-inventory plus plan-wide Always Free capacity check. After a successful apply, the
existing self-hosted omni-runner group runs the private health job and uses only Omni's
service-account API to generate job-local, short-lived Talos and Kubernetes access. It
requires oci-lab to become
RUNNING Ready within 90 seconds, then requires exactly two Talos nodes on the requested
Talos version, a ready Kubernetes API, exactly two Ready Kubernetes nodes, and the requested
Kubernetes server version. Missing credentials, generated access, a timed-out probe, an old
healthy node, or a topology mismatch fails the deployment; RUNNING Ready alone is never a
successful release result. It requires OMNI_SERVICE_ACCOUNT_KEY for the existing Omni
service account; a missing or unauthorized credential fails the deployment. No personal
Omni/PGP credentials are used or permitted by this workflow.
When omni_ready = true:
- Nodes boot Talos Linux (custom OCI image from oci-free-tier-images)
user_datainjects a Talos MachineConfig that joins Omni via SideroLink- Omni detects the nodes and provisions the cluster
- Argo CD GitOps is managed by syscode-homelab-gitops-apps
- Ampere A1: 2 OCPUs + 12 GB RAM total (ARM64, flexible — split across up to 2 instances)
- E2.1.Micro: 2 instances × 1/8 OCPU + 1 GB RAM (AMD/x86, fixed shape — a separate Always Free pool, independent of the A1 allowance)
This module deploys the full Always Free allotment (two separate pools):
ampere_nodes: 2 x A1.Flex (Talos nodes, 1 OCPU / 6 GB each) → uses all 2 OCPUs / 12 GB of the A1 poolmicro_nodes: 2 x E2.1.Micro (AMD/x86, fixed 1/8 OCPU + 1 GB each — a pool separate from A1) → Micro #1 (oci-micro-01) is the public-subnet knockd SSH bastion (reserved IP141.147.87.29); Micro #2 is the Tailscale subnet router inoci-vpn-subnetadvertising REDACTED_PRIVATE_SUBNET (opt in viaenable_oci_vpn_probe=true). The VPN router's egress path is the VPN-subnet NAT gateway route, NOT the bastion. No instance carries two VNICs.
This module deploys the full Always Free allotment:
ampere_nodes: 2 x A1.Flex (Talos nodes) → uses all 2 OCPUs / 12 GBmicro_nodes: 1 x E2.1.Micro (oci-micro-01); the bastion runs VPN probes through scoped DRG routes without requiring a second Micro instance
- Block volumes: 200 GB total (includes all boot volumes — shared across Ampere and Micro instances)
- Object storage: 20 GB
Current footprint: 2×50 (Ampere) + 2×50 (Micro) = 200 GB used, at the hard limit.
- VCNs: 2
- Load balancer: 1 (10 Mbps)
- Egress: 10 TB/month
Normal — Ampere instances are highly contested. The CI deploy workflow retries automatically. For manual deployments:
- Re-run
tofu apply— OCI eventually allocates capacity - Try a different availability domain within the same region
- Try off-peak hours
200 GB includes all boot volumes. Example allocations:
- 4× Ampere at 47 GB = 188 GB (leaves 12 GB)
- 3x Ampere at 50 GB = 150 GB (default)
- 3x Ampere + 1x Micro at 50 GB = 200 GB (maxed; opt in with
micro_nodes = [{}])
- oci-free-tier-images — Custom OS images (Talos, Debian) built for OCI import
- syscode-homelab-gitops-apps — Argo CD GitOps apps for the Talos cluster