Oracle Cloud Always Free — Ampere A1 Auto-Provisioning
Overview
Automated, zero-risk provisioning system running on an existing Oracle Cloud Free Tier Micro VM (
144.21.49.153). It polls the OCI API on a safe 5-minute schedule to secure an Ampere A1 ARM Compute instance (2 OCPUs, 12 GB RAM) the moment capacity becomes available in the London region.
1. Background & Problem Statement
Oracle Cloud Infrastructure (OCI) offers one of the most generous free tiers in cloud computing. However, the high-performance Ampere A1 (ARM) instances are almost perpetually in high demand, resulting in the error:
Out of host capacityBecause regular free-tier accounts share a heavily throttled capacity pool, manual attempts via the web console rarely succeed.
Why Not Upgrade to “Pay As You Go” (PAYG)?
While PAYG accounts get higher provisioning priority:
- The upgrade is 100% permanent and irreversible. Oracle does not provide any option to downgrade back to a pure Free Tier account.
- Financial Risk: If a non-free resource is accidentally created or storage exceeds 200 GB, the card on file is charged.
- Pure Free Tier Guarantee: Pure Free accounts are physically incapable of incurring charges. If a paid resource is attempted, Oracle rejects the request outright.
Decision
Stay on the pure Free Tier to ensure 100% safety and zero financial risk, and use automated API polling with rate-limiting protection to grab capacity as soon as another user releases it.
2. Oracle Cloud Free Tier Quotas (Current)
Recent Quota Changes
Earlier guides reference 4 OCPUs and 24 GB RAM. Oracle has officially adjusted the Always Free Ampere A1 quota to 2 OCPUs and 12 GB RAM. Attempting to request 4 OCPUs on a pure Free Tier tenancy will fail.
| Service | Always Free Allowance | In Use Before | Allocated by Automation |
|---|---|---|---|
| Ampere A1 Cores | 2 OCPUs (1,500 OCPU hrs/mo) | 0 | 2 OCPUs |
| Ampere A1 Memory | 12 GB RAM (9,000 GB hrs/mo) | 0 | 12 GB |
| AMD Micro VMs | 2 instances (VM.Standard.E2.1.Micro) | 1 (Vaultwarden) | 0 (1 still available) |
| Block / Boot Storage | 200 GB total | ~50 GB | 50 GB (100 GB still free) |
| Volume Backups | 5 backups | 0 | 0 |
| Object Storage | 20 GB standard | 0 | 0 |
| Outbound Transfer | 10 TB / month | Minimal | Included |
3. Architecture & Workflow
The solution runs directly on the user’s existing 24/7 AMD Micro instance, consuming virtually zero additional resources.
flowchart TD Cron["Cron Job (Every 5 Minutes)"] --> CheckFlag{"Does instance_created.flag exist?"} CheckFlag -- Yes --> Exit["Exit immediately (Stop)"] CheckFlag -- No --> Auth["Authenticate via OCI API SDK (RSA PEM)"] Auth --> Discovery["Discover Subnet & Latest Ubuntu 24.04/26.04 ARM Image"] Discovery --> AD1["Attempt Launch in UK-LONDON-1-AD-1"] AD1 -- "Out of host capacity" --> AD2["Attempt Launch in UK-LONDON-1-AD-2"] AD1 -- "Success" --> Success["Save instance_created.flag & Exit"] AD2 -- "Out of host capacity" --> Wait["Log notice & Sleep until next cron"] AD2 -- "Success" --> Success
Key Technical Findings in uk-london-1
- London Availability Domains: London has 3 ADs (
AD-1,AD-2,AD-3). - Hardware Distribution: Ampere A1 (
VM.Standard.A1.Flex) hardware only exists in AD-1 and AD-2. AD-3 returns404 Not Foundfor A1 shapes. - The script filters specifically for AD-1 and AD-2 to prevent wasted requests.
4. Deployed Components
A. Environment & Tools
- Host VM:
144.21.49.153(instance-20250727-1959-vaultwarden) - OS: Ubuntu 24.04 LTS (x86_64)
- Virtualenv:
/home/ubuntu/.oci-env - CLI / SDK:
oci-cli 3.92.1withociPython SDK 2.185.2
B. OCI Authentication Setup
- Configuration File:
/home/ubuntu/.oci/config[DEFAULT] user=ocid1.user.oc1..aaaaaaaaum2rxkbmzsv6ys2jmnjmfj5ps6ehwz5cz4j3tp2o3k436kw6xt4a fingerprint=fa:93:7c:f8:06:64:66:8f:49:58:5a:a8:dd:52:ef:b8 tenancy=ocid1.tenancy.oc1..aaaaaaaa3qmmiutixriu2ey7z42mpok3xxy4slppn2yk526ylxnlhknnzdha region=uk-london-1 key_file=/home/ubuntu/.oci/oci_api_key.pem - Key Format: Traditional OpenSSL RSA (PKCS#1) format (
-----BEGIN RSA PRIVATE KEY-----), required by OCI’s API request signer.
C. The Provisioning Script (/home/ubuntu/launch_arm.py)
#!/home/ubuntu/.oci-env/bin/python3
import os
import sys
from datetime import datetime
FLAG_FILE = "/home/ubuntu/.oci/instance_created.flag"
LOG_FILE = "/home/ubuntu/oci_launch.log"
def log(msg):
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
line = f"[{timestamp}] {msg}"
print(line)
try:
with open(LOG_FILE, "a") as f:
f.write(line + "\n")
except Exception:
pass
# 1. Exit if already provisioned
if os.path.exists(FLAG_FILE):
log("Instance already created previously. Exiting.")
sys.exit(0)
try:
import oci
except ImportError:
log("Error: oci library not found.")
sys.exit(1)
config_path = os.path.expanduser("~/.oci/config")
if not os.path.exists(config_path):
log("Error: ~/.oci/config does not exist.")
sys.exit(1)
config = oci.config.from_file(config_path)
compute_client = oci.core.ComputeClient(config)
network_client = oci.core.VirtualNetworkClient(config)
identity_client = oci.identity.IdentityClient(config)
compartment_id = config["tenancy"]
# 2. Extract SSH public key
ssh_key_path = os.path.expanduser("~/.ssh/authorized_keys")
with open(ssh_key_path, "r") as f:
ssh_authorized_key = f.read().strip()
# 3. Discover Subnet
try:
subnets = network_client.list_subnets(compartment_id=compartment_id).data
if not subnets:
log("Error: No subnets found in compartment.")
sys.exit(1)
subnet_id = subnets[0].id
except Exception as e:
log(f"Error finding subnet: {e}")
sys.exit(1)
# 4. Find latest Ubuntu ARM image
try:
images = compute_client.list_images(
compartment_id=compartment_id,
operating_system="Canonical Ubuntu",
shape="VM.Standard.A1.Flex",
sort_by="TIMECREATED",
sort_order="DESC"
).data
arm_images = [img for img in images if "aarch64" in img.display_name.lower() or "arm64" in img.display_name.lower()]
selected_image = arm_images[0] if arm_images else images[0]
except Exception as e:
log(f"Error finding image: {e}")
sys.exit(1)
# 5. Filter ADs supporting A1
try:
ads = identity_client.list_availability_domains(compartment_id=compartment_id).data
except Exception as e:
log(f"Error listing availability domains: {e}")
sys.exit(1)
valid_ads = []
for ad in ads:
try:
shapes = compute_client.list_shapes(compartment_id=compartment_id, availability_domain=ad.name).data
if any(s.shape == "VM.Standard.A1.Flex" for s in shapes):
valid_ads.append(ad.name)
except Exception:
pass
if not valid_ads:
valid_ads = ["uqhT:UK-LONDON-1-AD-1", "uqhT:UK-LONDON-1-AD-2"]
# 6. Try launching across valid ADs
for ad_name in valid_ads:
log(f"Checking capacity in {ad_name}...")
launch_details = oci.core.models.LaunchInstanceDetails(
compartment_id=compartment_id,
availability_domain=ad_name,
shape="VM.Standard.A1.Flex",
shape_config=oci.core.models.LaunchInstanceShapeConfigDetails(
ocpus=2.0,
memory_in_gbs=12.0
),
display_name="instance-ampere-a1",
image_id=selected_image.id,
create_vnic_details=oci.core.models.CreateVnicDetails(
subnet_id=subnet_id,
assign_public_ip=True
),
metadata={
"ssh_authorized_keys": ssh_authorized_key
},
source_details=oci.core.models.InstanceSourceViaImageDetails(
source_type="image",
image_id=selected_image.id,
boot_volume_size_in_gbs=50
)
)
try:
response = compute_client.launch_instance(launch_details)
instance = response.data
log(f"🎉 SUCCESS! Instance launched in {ad_name}!")
log(f"Instance OCID: {instance.id}")
log(f"Display Name: {instance.display_name}")
with open(FLAG_FILE, "w") as f:
f.write(f"Created instance {instance.id} in {ad_name} at {datetime.now().isoformat()}\n")
# Trigger dual alerts (OCI Email + Ntfy push)
send_alerts(instance.id, ad_name, instance.display_name)
sys.exit(0)
except oci.exceptions.ServiceError as se:
if "out of host capacity" in se.message.lower() or se.status in (429, 500):
log(f"Notice: Out of host capacity in {ad_name}.")
else:
log(f"Notice: {se.message.strip()} in {ad_name} (HTTP {se.status})")
except Exception as e:
log(f"Unexpected error in {ad_name}: {e}")
log("All eligible ADs currently out of capacity. Will retry in next scheduled run.")D. Automated Alerting Channels (Dual Delivery)
The script fires two distinct alerts the instant the VM is provisioned:
- Oracle Cloud Email (
gs9339@gmail.com):- Publishes directly to the active OCI Notification Topic
Altert(ocid1.onstopic.oc1.uk-london-1.amaaaaaaoba6llyadexdotznw5zjp7azr2zgdwf4fz6cnbtp77wgwinw56pq). - Delivers an official Oracle Cloud notification email directly to your inbox with instance details.
- Publishes directly to the active OCI Notification Topic
- Instant Push Alert via Ntfy:
- Pushes to
https://ntfy.sh/oracle-alert-gs9339-arm. - You can open this link in any browser or add it to the free mobile
ntfyapp to receive instant push alerts.
- Pushes to
E. Cron Job
Configured under the ubuntu user:
SUPPRESS_LABEL_WARNING=True
*/5 * * * * /home/ubuntu/launch_arm.py5. Operations & Runbook
How to Monitor the Process
SSH into your micro instance from your local terminal:
ssh -i ~/.ssh/oracle_vm.key ubuntu@144.21.49.153-
View live or recent attempt logs:
tail -n 25 -f ~/oci_launch.log -
Check if the instance has been created:
cat ~/.oci/instance_created.flag -
Check the instance in OCI CLI manually:
oci compute instance list --compartment-id ocid1.tenancy.oc1..aaaaaaaa3qmmiutixriu2ey7z42mpok3xxy4slppn2yk526ylxnlhknnzdha --query "data[].{Name:\"display-name\", State:\"lifecycle-state\", Shape:shape}" --output table
How to Stop the Automation
If you ever want to stop the script from running:
crontab -rConnecting to Your New Instance
Once the script succeeds:
- Find the new instance’s Public IP from the OCI Console or via:
oci compute instance list-vnics --instance-id <INSTANCE_OCID> --query "data[0].\"public-ip\"" --raw-output - Connect using your existing key:
ssh -i ~/.ssh/oracle_vm.key ubuntu@<NEW_INSTANCE_IP>
6. Safety & Anti-Ban Summary
Why This Will Never Get Banned
- Standard API Integration: Uses the official Oracle Python SDK with cryptographic request signing identical to official Oracle tooling (Terraform, Ansible, OCI Console).
- 5-Minute Cadence: 1 attempt every 300 seconds represents an infinitesimal 0.0033 requests/sec, far below Oracle’s rate limiter (HTTP 429 threshold is ~50 requests/sec).
- Self-Terminating: Permanently halts execution via lockfile the second capacity is allocated.