Evaluating an OCI always-free migration — and why we stayed on AWS
Tried to move the backend off AWS (12-month free) onto Oracle Cloud's always-free Arm tier (free forever). Wrote and adversarially-verified the OCI Terraform, but hit three walls — a VCN-count limit, a manual-deletion dependency-order trap, and the chronic 'Out of host capacity' for Always-Free Ampere A1. The PAYG upgrade that fixes capacity stalled too. Decision: hold the migration, keep the workload on AWS free tier, and keep the validated OCI config for a future retry.
The only future cost on the current setup is AWS RDS after its 12-month free window. OCI's always-free tier (an Arm Ampere A1 VM, 4 OCPU / 24 GB, free forever) looked like the way to make it $0 permanently — run the app's Docker image plus a self-hosted Postgres on one always-free VM. This is the evaluation, and why we didn't finish the move.
The config was the easy part
Wrote the OCI Terraform from scratch — provider auth via ~/.oci/config, and the full network path OCI makes you build by hand (VCN, internet gateway, route table, public subnet, security list), plus the A1.Flex instance with the Ubuntu image resolved from a data source. An adversarial review of the draft caught a real bug — filtering oci_core_images by shape alone can still return an x86 image, so it added a display_name regex on aarch64 — and added validation blocks to keep ocpus/memory within the 4 OCPU / 24 GB always-free ceiling. terraform validate passed.
Three walls on apply
1. VCN-count limit.
400-LimitExceeded, The following service limits were exceeded: vcn-count.The tenancy was already at its VCN limit — a leftover json-claude-vnic from an earlier automated attempt. Had to delete it.
2. Manual deletion is a dependency-order puzzle. Deleting that VCN by hand failed because its route table still pointed 0.0.0.0/0 at the internet gateway:
The Route Table ... references the Internet Gateway ... You must remove the reference to proceed.Remove the route rule first, then the gateway, then the VCN. (terraform destroy does this ordering for you — the manual pain is exactly the argument for letting Terraform own its resources.)
3. Out of host capacity — the real blocker. With the VCN slot freed, the networking created cleanly, but the instance didn't:
500-InternalError, Out of host capacity.This is the well-known Always-Free Ampere A1 shortage: Oracle has no free A1 hosts to hand out in ca-toronto-1 (a single-AD region, so there's no other availability domain to try). Shrinking the request from 2 OCPU / 12 GB to 1 OCPU / 6 GB didn't help — capacity errors are about host availability, not size.
The standard escape hatches are a retry loop until a slot frees, or upgrading to Pay As You Go (which keeps the always-free A1 free but raises capacity priority). The PAYG upgrade flow itself stalled in the console.
The decision
Held the migration; kept the workload on AWS free tier. The reasoning was a time-vs-goal trade-off, not a technical dead-end: the goal is a job hunt where AWS experience outweighs OCI, AWS is already live and free for 12 months, and fighting Always-Free Arm capacity (plus a balky billing upgrade) is an open-ended time sink. OCI's always-free resources are real — but the access is rate-limited behind paid usage, which is its own lesson about reading "free forever" claims.
What's kept: the OCI Terraform is written, reviewed, and validate-clean in infra-oci/, so a future retry (a retry loop, an off-peak window, or PAYG) is a terraform apply away — no rework.