What You Can Self-Host on Oracle Cloud Free Tier (2026)
What actually fits on Oracle Cloud free tier after the June 2026 cut: Ghost, Vaultwarden, Syncthing, monitoring, and a small AI model, sized in real RAM.
Last updated: July 27, 2026
Status as of this writing: Oracle quietly cut the Always Free Ampere A1 allocation from 4 OCPUs / 24 GB RAM to 2 OCPUs / 12 GB RAM on June 15, 2026. No blog post, no email, an edited documentation page and nothing else. If you're reading an older guide (including some still-circulating 2026 posts) that budgets against 24 GB, check your own console before following its numbers. Everything below is sized for the current 2 OCPU / 12 GB reality, plus the two small AMD Micro instances that are unaffected by the change.
A free tier that quietly loses half its headroom is still worth building on. It means the sizing conversation matters more than it used to, and CPU, not RAM, ends up being the tighter constraint on two cores.
What Oracle gives you for free
The Always Free resources relevant to a self-hosting stack:
- Ampere A1 Flex (ARM): 2 OCPUs and 12 GB RAM total, split across one to four instances as you choose. This is down from 4/24 as of June 2026.
- AMD Micro instances: two of them, each 1/8 OCPU and 1 GB RAM. Small, but free on its own terms and separate from the Ampere allocation.
- Block storage: 200 GB total, shared across your instances.
- Outbound bandwidth: 10 TB per month.
- Reclamation risk: Oracle's current published policy reclaims an Always Free compute instance if, over a 7-day period, its 95th-percentile CPU, network, and (for A1 shapes) memory utilization all stay under 20%. A stack that's serving real traffic rarely trips this; an instance you spun up once and forgot about is exactly what the policy targets.
- Regional availability: Ampere A1 capacity is tightly constrained in popular regions. Frankfurt and Singapore tend to provision within minutes; US regions can sit at "out of host capacity" for hours or days. Budget for that when you're planning a migration deadline.
What "2 OCPU" means on Ampere A1. On Oracle's x86 shapes, one OCPU is a full physical core with hyperthreading, billed as the equivalent of two vCPUs, so a workload can lean on simultaneous multithreading to squeeze more out of contended cores. Ampere A1 doesn't work that way: the Ampere Altra processor behind it has no hyperthreading, so one OCPU is exactly one physical core and one thread, nothing shared. Two OCPUs on this tier means two real cores and nothing more to borrow from. That's why CPU contention shows up immediately on this tier rather than being smoothed over by a hyperthreading scheduler the way it might be on an equivalent x86 box.
Two honest limitations before the stack: first, ARM64 isn't universal. Most actively maintained self-hosting images ship multi-arch builds, but some don't. Ghost's core image runs fine on ARM64; its optional ActivityPub and Traffic Analyzer add-ons are AMD64-only as of this writing, so check before assuming a feature works. Second, 2 shared OCPUs is a real ceiling once more than one service wants the CPU at the same time. RAM sizing matters, but CPU contention is what bites on this tier.
The stack: one Ampere instance, one AMD Micro
Put everything internet-facing on the single Ampere A1 instance, and use one AMD Micro purely as an external watchdog, something that checks whether the main box is reachable from outside its own failure domain. The second Micro is spare capacity for later.
| Service | Job | Real RAM cost |
|---|---|---|
| Caddy | TLS termination, reverse proxy | Under 50 MB |
| Ghost + MySQL | Blog/CMS (this article's own platform) | Roughly 750 MB in production use |
| Vaultwarden | Password manager, Bitwarden-compatible | ~10 MB idle |
| Syncthing | Peer-to-peer file sync, no central server needed | 150–300 MB typical, spikes higher during large scans |
| Ollama (3B model, Q4) | Lightweight local AI for personal use | Roughly 3–4 GB |
| Uptime Kuma + Beszel agent | Uptime checks plus host/container metrics | 150–180 MB combined |
| OS + Docker overhead | Everything else | Roughly 500 MB |
That totals somewhere around 5–6 GB of the 12 GB available, leaving real headroom rather than a stack that fits with nothing to spare. The number that won't show up in a RAM table is CPU: an AI response generating tokens, a MySQL query, and a Syncthing hash scan happening at the same moment are competing for two cores, and that's where a personal Oracle box slows down, not memory pressure.
Running Vaultwarden already and want the full hardening pass? Our dedicated Vaultwarden guide covers backup strategy, admin panel lockdown, and the HTTPS requirements the official clients enforce. Take a look at our complete Vaultwarden self-hosting guide.
Why each piece is on this list
Ghost runs the blog you're reading right now, so it earns a place as a real-world example rather than a theoretical pick. The official minimum is 1 GB of RAM; in production with MySQL running alongside it, a well-known self-hosting write-up clocks its own long-running instance at 214 MB for Ghost and 538 MB for MySQL, comfortably under 1 GB combined. Budget for 2 GB if you expect real traffic or plan to turn on the newsletter feature, which adds Mailgun and additional background jobs.
Vaultwarden is the easiest sizing decision in this list. It's a Rust reimplementation of the Bitwarden server, and our own Vaultwarden instance (the one covered in our dedicated guide) idles around 10 MB of RAM serving eight active users, less than a browser tab. There's no meaningful tradeoff here: it's compatible with every official Bitwarden client, and the resource cost rounds to zero next to everything else on this instance.
Syncthing covers file synchronization without needing a central server the way Nextcloud does; it syncs directly between your devices and this instance acts as one more always-on peer. That's a deliberate tradeoff for this tier: Nextcloud gets you a web UI, calendar and contacts sync, and a proper multi-user file store, all of which cost meaningfully more RAM than 12 GB comfortably supports alongside everything else here. If you have RAM to spare on a bigger box later, our productivity-stack guide covers Nextcloud, Immich, Jellyfin, and Paperless-ngx together. On this tier, Syncthing does the one job (getting files onto more than one device) for a fraction of the cost.
Idle RAM use is modest, but it's not flat: Syncthing keeps file indexes in memory, and both very large individual files and folders with hundreds of thousands of small files can push memory use well above idle during an initial scan, in some reported cases into the gigabytes on a large enough folder tree. If you're syncing a large photo library or a folder with a huge file count, set maxConcurrentWrites to 1 and sync folders sequentially rather than in parallel; both are documented tuning options that trade sync speed for a lower peak. On a 12 GB instance already running an AI model, that peak matters more than it would on a dedicated NAS.
Ollama is the one service constrained by this tier's hardware and not only its RAM budget. There's no GPU on Oracle's free tier at all, so inference runs on the Ampere A1's ARM cores. A quantized 7B model is technically possible (public benchmarks put a Q4_K_M 7B model around 5–8 tokens per second on a 4-OCPU A1 instance), but at 2 OCPUs, a smaller 3B model at Q4 quantization is the more honest fit for anything resembling a responsive personal assistant, and it leaves the other two-thirds of your CPU budget for Ghost and Syncthing to use.
Uptime Kuma paired with Beszel is the monitoring choice that matters most on a RAM-constrained tier. A combined Prometheus, Grafana, and node_exporter stack, the setup in our own monitoring guide, runs 800 MB or more at idle before it's watched a single request; Uptime Kuma plus a Beszel agent covers external uptime checks and internal host/container metrics for roughly 150–180 MB combined. On a 24 GB instance, or a paid box with RAM to spare, Prometheus and Grafana's deeper PromQL querying and alerting rules are worth the extra memory. On 12 GB shared with an AI model, Uptime Kuma is the choice between having monitoring at all and not.
The Layout
Oracle free tier architecture · SVGEverything internet-facing sits behind Caddy on the Ampere instance. The AMD Micro doesn't run any of the stack; its only job is checking the Ampere box from a separate failure domain, so a problem with the main instance doesn't also take down the thing telling you about the problem.
A resource-bounded Compose file
services:
caddy:
image: caddy:latest
restart: unless-stopped
ports:
- "443:443"
- "80:80"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
networks:
- stack_net
ghost:
image: ghost:5
restart: unless-stopped
environment:
- database__client=mysql
- database__connection__host=ghost-db
- database__connection__user=ghost
- database__connection__password=${GHOST_DB_PASSWORD}
- database__connection__database=ghost
- url=https://blog.yourdomain.com
volumes:
- ghost_content:/var/lib/ghost/content
deploy:
resources:
limits:
memory: 1g
networks:
- stack_net
ghost-db:
image: mysql:8
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=ghost
- MYSQL_USER=ghost
- MYSQL_PASSWORD=${GHOST_DB_PASSWORD}
volumes:
- ghost_db:/var/lib/mysql
deploy:
resources:
limits:
memory: 700m
networks:
- stack_net
vaultwarden:
image: vaultwarden/server:latest
restart: unless-stopped
environment:
- ADMIN_TOKEN=${VAULTWARDEN_ADMIN_TOKEN}
- SIGNUPS_ALLOWED=false
volumes:
- vaultwarden_data:/data
deploy:
resources:
limits:
memory: 128m
networks:
- stack_net
syncthing:
image: syncthing/syncthing:latest
restart: unless-stopped
environment:
- STMAXCONCURRENTWRITES=1
volumes:
- syncthing_data:/var/syncthing
deploy:
resources:
limits:
memory: 768m
networks:
- stack_net
ollama:
image: ollama/ollama:latest
restart: unless-stopped
volumes:
- ollama_data:/root/.ollama
deploy:
resources:
limits:
memory: 4g
networks:
- stack_net
uptime-kuma:
image: louislam/uptime-kuma:2
restart: unless-stopped
volumes:
- uptime_kuma_data:/app/data
deploy:
resources:
limits:
memory: 150m
networks:
- stack_net
networks:
stack_net:
external: true
volumes:
caddy_data:
ghost_content:
ghost_db:
vaultwarden_data:
syncthing_data:
ollama_data:
uptime_kuma_data:
Caddy stays the only exposed port on the instance. Everything else, Ghost, Vaultwarden, Syncthing's sync protocol, Ollama's API, and the Uptime Kuma dashboard, sits behind it on the internal Docker network, reachable by subdomain rather than by port number.
Three Important Performance Fixes
The Compose file above will run. These three changes are what keep it running well once Syncthing hits a big scan or Ollama is mid-response while someone's reading the blog.
Add swap, even on a "fast enough" instance. Ampere A1 has no swap configured by default, and 12 GB with several services and an AI model loaded leaves less margin than it looks like on paper. A memory spike that would slow things down with swap available becomes an OOM-killed container without it, and Docker's OOM killer doesn't pick politely; it can take out MySQL as easily as the process that caused the spike. A modest swap file (4 GB is reasonable against 12 GB of RAM) plus a low swappiness value gives the kernel somewhere to put cold pages before it starts killing containers:
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
sudo sysctl vm.swappiness=10
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
Swap on a boot volume backed by network-attached block storage is slower than swap on local NVMe, so this is a safety margin for spikes, not a substitute for the RAM budget in the table above. Don't plan to run the stack out of swap day to day.
Tune MySQL down from its defaults. Out of the box, MySQL 8's innodb_buffer_pool_size defaults to 128 MB, which is conservative enough to be safe here, but the rest of its default configuration still assumes more headroom than a 12 GB shared host has to give. A small my.cnf override keeps Ghost's database from creeping past the budget in the resource table:
[mysqld]
innodb_buffer_pool_size=256M
max_connections=50
table_open_cache=200
thread_cache_size=8
Mount this as /etc/mysql/conf.d/low-memory.cnf in the ghost-db service. It won't matter for a low-traffic personal blog, and it's the difference between MySQL sitting at 700 MB and MySQL slowly climbing toward whatever's left on the box during a traffic spike.
Cap Ollama's concurrency instead of leaving it to load-balance itself. Ollama's defaults assume it can decide how many models to keep loaded and how many requests to serve in parallel, which is a reasonable default on a dedicated GPU box and a bad one on a 2-core ARM instance sharing memory with everything else here. Setting explicit limits keeps one long generation from queuing up a second one and doubling memory use:
OLLAMA_NUM_PARALLEL=1
OLLAMA_MAX_LOADED_MODELS=1
OLLAMA_MAX_QUEUE=4
Add these as environment variables on the ollama service in the Compose file. OLLAMA_NUM_PARALLEL=1 means the second request waits instead of doubling memory use by running alongside the first; on a personal instance where you're the only user, that queueing is invisible in practice and the memory ceiling it buys is not.
Want the general self-hosting stack playbook, beyond the free-tier version? Our shared server-prep and reverse-proxy pattern applies whether you're on a free Ampere instance or a paid box. Check out the Mailcow Guide!
Lessons from running this
Check ARM64 support before you deploy, not after. Most of the images above have solid ARM builds. A few popular self-hosting tools still don't, or only support ARM in a beta tag, and finding that out mid-migration is worse than checking before the first docker compose up:
docker manifest inspect ghost:5 | grep -A2 '"architecture"'
Look for "architecture": "arm64" in the output alongside "amd64". If it's missing, the image is x86-only and won't run on the Ampere instance at all, or will run under slow QEMU emulation if Docker's configured to allow it, which defeats the point of a free ARM box.
Watch the reclamation window if traffic is thin. A personal blog with a handful of daily visitors, a password vault only you touch, and a sync client that mostly sits idle can plausibly dip under Oracle's 20% utilization thresholds across a full week. Real, if modest, usage from an actual project protects you here better than any artificial workaround, and running an artificial load generator specifically to dodge the reclamation policy sits in a gray area against Oracle's terms of service that isn't worth the risk for a free instance.
Pin your image tags once the stack is stable. latest is fine while you're getting things running; once Ghost, Vaultwarden, and Ollama are doing real work, an unannounced upstream change landing on a routine docker compose pull is a worse failure mode than a slightly stale version.
Back up the volumes that matter. Ghost's content volume and MySQL data, Vaultwarden's data directory, and Syncthing's config and index. Model weights for Ollama are re-downloadable and not worth backing up; Uptime Kuma's history is nice to keep but not critical if it's lost.
The Compose file, ready to adapt
The full stack above, Caddy, Ghost, Vaultwarden, Syncthing, Ollama, and Uptime Kuma, is a lot to retype by hand. Grab the complete docker-compose.yml and a matching .env template below, drop in your own domain and secrets, and you're running rather than transcribing.
Get the Compose file and .env template
Enter your email and we'll send the exact configuration from this article, including the resource limits, ready to paste onto a fresh Ampere A1 instance.
If you outgrow the 12 GB ceiling, that's a genuine milestone, not a failure of the free tier. It means the stack is doing enough real work that a small paid VPS or dedicated box starts paying for itself in time saved rather than money spent, and at that point the free tier has already done its job: it got you running for the cost of an afternoon instead of a monthly bill.

