Oracle Cloud's Always Free tier is, quietly, one of the best deals in cloud computing: 2 Arm OCPUs and 12 GB of RAM, free forever, no trial clock. That's still enough for a homelab node, a Docker stack, a VPN, or a game server - running 24/7 without paying a cent or buying hardware.
(Update, June 2026: Oracle quietly halved this from 4 OCPU / 24 GB to 2 OCPU / 12 GB on 15 June 2026, with no announcement. Pay-as-you-go accounts reportedly kept the old 4/24 for free, but Oracle hasn't confirmed that publicly.)
There's just one problem standing between you and that box, and if you've tried you already know its name:
ServiceError:
code: InternalError
message: Out of host capacity.
Those Arm hosts are in such heavy demand that creating an instance usually fails. US regions can be dry for hours or days. Even busy EU/APAC regions dip in and out. Capacity frees up in small, random windows β and whoever happens to be retrying at that exact second gets it.
I spent the better part of a week refreshing the OCI console like it was a concert ticket drop. Then I did the obvious thing: I wrote a script to play the lottery for me, on a timer, and walked away. It worked β it caught me a 4 OCPU / 24 GB A1 instance. So I cleaned it up and open-sourced it.
π github.com/alexpua/oci-arm-catcher (MIT)
[GIF: terminal showing "Out of host capacity⦠retrying" a few times, then "SUCCESS! Instance created"]
The idea is dumb on purpose
There's no clever exploit here. The tool just calls the official OCI CLI command β the exact same oci compute instance launch you'd run by hand β in a polite loop, and reacts to the result:
loop:
oci compute instance launch --shape VM.Standard.A1.Flex ...
ββ success β parse the instance OCID, desktop notification, exit
ββ "out of capacity" β wait, (optionally rotate AD), retry
β InternalError / LimitExceeded / TooManyRequests / timeout
ββ any other error β print it, notify, STOP
The only part that actually matters is the last two branches. A naive
while true; do launch; sleep; done loop is worse than useless: when your config has a typo β wrong subnet OCID, an x86 image, an auth problem β it will cheerfully retry that broken request forever and you'll never notice.
So the catcher splits errors into two buckets:
-
Transient / capacity (
Out of host capacity,InternalError,TooManyRequests, timeouts) β keep trying, this is the whole point. -
Everything else (
NotAuthorizedOrNotFound,LimitExceeded, bad image) β stop immediately and tell the human.
That single distinction is the difference between "I left it running overnight and woke up to a server" and "I left it running overnight and woke up to 4,000 copies of the same error."
Things I added once it was more than a personal hack
Readable error output. The OCI CLI prints errors as a JSON blob to stderr.
My first version grepped for "message" and frequently printed ?: ? when the shape didn't match. Now a tiny Python helper parses the JSON properly and always falls back to something human-readable:
-> InternalError: Out of host capacity.
Multi-AD rotation. Regions like Ashburn, Phoenix and Frankfurt have three Availability Domains, and capacity can appear in any one of them. Give the catcher all three and it cycles through on each retry, multiplying your chances:
AVAILABILITY_DOMAINS="Abcd:US-ASHBURN-1-AD-1,Abcd:US-ASHBURN-1-AD-2,Abcd:US-ASHBURN-1-AD-3"
A config-discovery helper. The genuinely annoying part of OCI isn't the API, it's finding all the OCIDs (compartment, subnet, image, availability domain). So there's a read-only get-config script that prints them, formatted to paste straight into your .env. It launches nothing β just reads metadata.
Cross-platform. Bash for macOS/Linux, plus a native PowerShell port for Windows (with toast notifications). Or run the bash version under WSL2 β your call.
Desktop notifications. Because the whole pitch is "start it and forget it," it pings you the moment it lands β osascript on macOS, notify-send on Linux, a toast on Windows.
Tests. It mocks the oci CLI and asserts the important behaviors: retries on capacity errors, stops on auth errors, parses the OCID on success, never prints ?: ?. bats for bash, Pester for PowerShell, ShellCheck β all in CI. For a 200-line shell script that's arguably overkill, but I wanted the "stop on real errors"
guarantee to be actually guaranteed.
Using it
git clone https://github.com/alexpua/oci-arm-catcher.git
cd oci-arm-catcher
cp .env.example .env
./scripts/get-config.sh # prints your OCIDs β paste into .env
# edit .env
nohup ./oci-arm-catcher.sh > catcher.log 2>&1 &
tail -f catcher.log
Then go do something else. When capacity opens up, you get a notification and a running instance.
A note on being a good citizen
This isn't a way to "beat" Oracle or bypass anything. It does exactly what you'd do by hand, just patiently and on an interval (default: every 5 minutes β not a hammer). You still have to stay inside your free-tier allowance. It's automation of a tedious manual task, nothing more.
Try it / break it
If you've been losing the capacity lottery, give it a run:
github.com/alexpua/oci-arm-catcher
Issues and PRs welcome β I'd especially love more notification backends (Telegram, Discord, Slack), smarter backoff, and region/AD presets.
And if it caught you a free server: a β on the repo helps the next person find it, and if you feel like it, you can buy me a coffee β.
Both totally optional β the tool's free either way.
Top comments (0)