In my previous post, I walked through setting up a zero-cost mobile Claude Code environment on Oracle Cloud’s Always Free tier. The VM, VCN, subnet, security list, and internet gateway take maybe 15 minutes to click through in the Oracle console, and you are up and running.
This is the type of thing that you might do once and never think about it again. Unless you blow it away. Or want a second one. Or want a friend to replicate your setup. Then you’re clicking through that console again, trying to remember which shape you picked and which security rules you set. Infrastructure-as-code exists precisely for this kind of thing.
For those cases, I put the whole stack in Terraform.
What It Provisions
The stack creates everything from Parts 2-3 of the setup guide:
- Compute instance — VM.Standard.A1.Flex (ARM), 4 OCPUs, 24 GB RAM, Ubuntu 24.04
- VCN with a public subnet
- Internet gateway and route table
- Security list — SSH ingress on port 22 (for initial setup, before Tailscale)
After terraform apply, you SSH in, install Tailscale, lock down the security list, and continue with the rest of the guide. The Terraform part replaces the manual console work; the post-provisioning steps (Tailscale, mosh, tmux, Claude Code, ntfy) stay the same.
Two Variables, That’s It
The whole configuration boils down to two required variables:
| Variable | What it is |
|---|---|
compartment_id |
Your OCI compartment OCID (usually your tenancy OCID) |
region |
OCI region, e.g. us-chicago-1 |
Everything else has sensible defaults. The availability domain and Ubuntu image are auto-discovered via OCI data sources, so you don’t need to hunt down region-specific OCIDs.
Before you start, make sure your OCI CLI is installed and your credentials are set up.
Run:
oci iam region-list
to verify your profile is active and authenticated. This helps avoid credential errors later.
git clone https://github.com/kevinmcmahon/oci-alwaysfree-tf-stack.git
cd oci-alwaysfree-tf-stack
cp terraform.tfvars.example terraform.tfvars
# edit terraform.tfvars: set compartment_id and region
terraform init
terraform plan
terraform apply
Your VM is running. SSH in and pick up at Part 3 of the setup guide:
ssh -i ~/.ssh/your-key ubuntu@$(terraform output -raw instance_public_ip)
Staying Inside the Free Limits
The defaults are tuned to use the full Always Free ARM allowance without exceeding it:
| Resource | Stack default | Always Free limit |
|---|---|---|
| ARM OCPUs | 4 | 4 total |
| Memory | 24 GB | 24 GB total |
| Boot volume | 47 GB | 200 GB total |
| Instances | 1 | Up to 4 ARM + 2 AMD |
If you change the defaults — say, bumping the boot volume to 200 GB — just make sure the total across all your instances stays within limits. And set up budget alerts at $0.01 actual spend so you’ll know immediately if anything crosses the free line.
Overridable Defaults
You probably won’t need to touch these, but they’re there:
| Variable | Default | Why you’d change it |
|---|---|---|
availability_domain_index |
0 |
Your preferred AD is full — try 1 or 2 |
ssh_public_key_path |
~/.ssh/id_ed25519.pub |
You use a different key |
instance_name |
claude-dev |
You want a different display name |
ocpus |
4 |
You’re splitting free resources across multiple instances |
memory_in_gbs |
24 |
Same — splitting across instances |
boot_volume_size_in_gbs |
47 |
You need more disk (up to 200 GB free total) |
assign_public_ip |
true |
Set to false after Tailscale is running |
Tearing Down and Rebuilding
This is the whole point of putting it in Terraform:
terraform destroy # gone
terraform apply # back
State tracks what was created, so destroy is clean. No orphaned resources, no mystery charges showing up a month later because you forgot to delete a route table.
The rebuild cycle is useful when Oracle releases new Ubuntu images, when you want to start fresh after experimenting, or when you want to help someone else set up their own instance. Fork the repo, set two variables, apply.
After Terraform: The Rest of the Stack
Terraform gets you the VM. Everything after that — Tailscale, UFW, mosh, tmux, Claude Code, ntfy hooks — is covered in the full setup guide. The short version:
- SSH in via the public IP
- Install Tailscale, authenticate, note your Tailscale IP
- Configure UFW to allow only the
tailscale0interface - Lock down the Oracle security list (remove SSH ingress)
- Optionally,
terraform applyagain withassign_public_ip = falseto drop the public IP - Install mosh, tmux, Node.js, Claude Code
- Set up ntfy notifications
From that point on, you connect via Termius on your phone, mosh over Tailscale, and Claude Code runs in a persistent tmux session. Push notifications tell you when Claude needs input. The VM runs 24/7 for free.
Links
- Terraform stack: kevinmcmahon/oci-alwaysfree-tf-stack
- Full setup guide: My Spin on Claude Code On-the-Go
- Oracle Always Free tier: oracle.com/cloud/free