TL;DR
A 1 GB VM in 2026 is more capable than the internet gives it credit for. With Caddy, SQLite, and a single-binary Go app or PHP-FPM in static mode, you can comfortably run Miniflux, Vaultwarden, Gitea (small), Shaarli, FreshRSS, or a Ghost blog on a $6 DigitalOcean droplet, a €4.50 Hetzner CX22, or a $5 Linode Nanode. The cliff is sharp: anything that ships a JVM, Chromium, Elasticsearch, or a full Rails app will OOM-kill itself before you finish the install wizard.
The 2026 cheap-tier landscape
The "$5 box" is now a slight misnomer. DigitalOcean's entry Basic Droplet is $6/mo (1 vCPU, 1 GB RAM, 25 GB SSD, 1 TB transfer). Linode/Akamai's Nanode 1 GB is still $5/mo (1 vCPU, 1 GB RAM, 25 GB NVMe, 1 TB transfer). Hetzner's CX22 is roughly €4.51/mo in EU regions and about $5.17 in Ashburn/Hillsboro, with 2 vCPU (shared AMD), 4 GB RAM, 40 GB NVMe, and 20 TB transfer.
That last line is not a typo. Hetzner gives you 4x the RAM, 2x the cores, and 20x the egress for roughly the same money. If you are building a price/performance spreadsheet, Hetzner CX22 wins almost every workload below the $20/mo line — which is exactly the kind of comparison the osscostcalc calculator on this site exists to make obvious.
| Provider | Plan | vCPU | RAM | Disk | Egress | Price |
|---|---|---|---|---|---|---|
| DigitalOcean | Basic | 1 | 1 GB | 25 GB SSD | 1 TB | $6.00 |
| Linode | Nanode 1GB | 1 | 1 GB | 25 GB NVMe | 1 TB | $5.00 |
| Hetzner | CX22 (EU) | 2 | 4 GB | 40 GB NVMe | 20 TB | ~€4.51 |
| Hetzner | CPX11 (US) | 2 | 2 GB | 40 GB NVMe | 20 TB | ~$4.85 |
For the rest of this guide I'll assume the harder constraint: 1 GB RAM, 1 vCPU, because that's what DigitalOcean and Linode actually give you at the entry tier. If you're on Hetzner, treat everything below as "with comfortable headroom."
What a fresh 1 GB box actually has free
Boot a Debian 12 minimal image. free -m will report somewhere around 880-920 MB available depending on the kernel and cloud-init shrapnel. By the time you've installed ufw, fail2ban, an SSH config, and unattended-upgrades, you're at roughly 820 MB free. That is your working budget.
Now layer a web stack:
- Caddy 2.x: 25-40 MB RSS idle, including the autocert manager.
- PHP 8.3 FPM with 4 workers: ~30 MB shared + ~25 MB per worker = ~130 MB.
- PostgreSQL 16 default config: 80-120 MB before you tune
shared_buffersdown. - MariaDB 11: 90-150 MB after you strip the performance schema.
- SQLite: zero. It's a library linked into your app.
- Redis with default settings: 8-12 MB idle.
The single most consequential decision you make on a 1 GB box is SQLite vs. a real RDBMS. Picking SQLite frees ~100 MB and one persistent process. On a 1 GB host that's not a rounding error — it's the difference between "runs fine" and "swap-thrashing during a backup."
The lightweight stack that just works
My default 1 GB recipe in 2026 is boring and effective:
- Caddy as the only public-facing process. Automatic TLS, HTTP/3, reverse proxy, static files. No nginx, no certbot cron job.
- SQLite with WAL mode for any app that supports it.
- The app as a single binary (Go, Rust) running as a systemd unit, or PHP-FPM in
pm = ondemandmode with a lowpm.max_children. - No Docker, or Docker only for one container. The daemon alone costs 80-120 MB and the overlay filesystem hides memory pressure from you.
- A 2 GB swap file on the same NVMe disk, with
vm.swappiness=10.
That stack, on a Hetzner CX22 or a $6 droplet, will host all of the following simultaneously without breathing hard:
- Miniflux (Go + SQLite or Postgres): ~25 MB RSS.
- Vaultwarden (Rust + SQLite): ~40 MB RSS.
- Shaarli (PHP + SQLite-ish flat files): ~5 MB per FPM worker.
- A static site or two via Caddy file_server.
Total resident usage: under 350 MB, leaving the kernel page cache plenty of room to actually cache your SQLite database in RAM — which is where SQLite's reputation for being "as fast as Redis for reads" comes from.
Tuning PHP-FPM for tiny boxes
The default pm = dynamic config on most distros assumes you have 8 GB and will happily spawn 50 children. On 1 GB this is suicide. Use:
pm = ondemand
pm.max_children = 4
pm.process_idle_timeout = 30s
pm.max_requests = 500
And enable OPcache with realistic settings:
opcache.enable=1
opcache.memory_consumption=64
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000
opcache.validate_timestamps=1
opcache.revalidate_freq=60
That 64 MB OPcache is shared across all workers, so it's a one-time cost. A WordPress install without OPcache eats ~45 MB per request in compile time alone; with it, you'll see 8-15 MB per worker and 3-4x the throughput on the same CPU.
Swap: friend, not crutch
The Linux mythology that "swap is bad on SSDs" is a holdover from spinning-disk and early-MLC days. On a modern NVMe-backed cloud volume, swap is the difference between a process being OOM-killed at 2 AM and it stuttering for 400 ms while a cold page is faulted back in.
On a 1 GB droplet I always create a 2 GB swap file:
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
sysctl -w vm.swappiness=10
sysctl -w vm.vfs_cache_pressure=50
Swappiness 10 means the kernel will strongly prefer evicting cached pages over anonymous process memory. That keeps your hot app pages resident and pushes idle daemons (looking at you, snapd, packagekitd, unattended-upgrades) into swap where they belong.
Note that Hetzner's CX line and DO's premium droplets use local NVMe, but DigitalOcean Basic droplets use network-attached SSD. Swap I/O on those is slower and counts toward your shared IOPS budget. Don't lean on swap as a primary workload mechanism there — use it as a safety net, not a tier of memory.
What breaks at 1 GB (the honest list)
These are the apps I have personally watched die or limp on a 1 GB box:
- GitLab CE / EE. The Omnibus install boots Puma + Sidekiq + Gitaly + Redis + Postgres + Nginx + Prometheus + Workhorse. Documented minimum is 4 GB and they mean it. Use Gitea or Forgejo instead.
- Elasticsearch / OpenSearch. The JVM default heap is 1 GB. You have 1 GB total. Math is not on your side. Meilisearch (Rust) or Typesense (C++) will do 80% of what you wanted in 80 MB.
- Mastodon. Ruby + Sidekiq + Streaming (Node) + Postgres + Redis + Elasticsearch (optional). Minimum 2 GB and even that is painful. Use GoToSocial (single Go binary, ~40 MB) for a personal fediverse presence.
- Nextcloud with previews enabled. Nextcloud itself runs in 1 GB if you're careful, but the preview generator and recognize app will pull in ffmpeg + a TensorFlow model and instantly OOM. Disable them, or run Pico/PicoCMS/Filebrowser instead.
- Anything with a bundled Chromium. That means Puppeteer, Playwright, headless screenshot services, Sentry's frontend ingest, BrowserBox. Chromium needs ~500 MB just to start.
- Discourse. 2 GB hard floor, 4 GB realistic. Use Flarum (PHP) if you need a forum.
- Plausible Analytics self-hosted. Requires ClickHouse, which wants ~1 GB heap on its own. Use Umami (Node + Postgres/MySQL) — it'll fit in 1 GB with Postgres tuned down.
A useful heuristic: if the install instructions tell you to install Docker Compose with more than three services, it won't fit.
Apps I trust on a 1 GB box in production
After running variations of this setup for clients and myself since 2019, here's my actual production list with measured idle RSS:
| App | Stack | Idle RAM | Notes |
|---|---|---|---|
| Miniflux | Go + SQLite | 25 MB | RSS reader, rock solid |
| Vaultwarden | Rust + SQLite | 40 MB | Bitwarden-compatible |
| Gitea (small) | Go + SQLite | 80-150 MB | Disable actions runner |
| Forgejo | Go + SQLite | 90-160 MB | Gitea fork, same footprint |
| FreshRSS | PHP-FPM + SQLite | 60 MB | With OPcache tuned |
| Shaarli | PHP-FPM + flat | 40 MB | Bookmarking |
| Ghost (small blog) | Node + SQLite | 180 MB | One of the heavier picks |
| Umami | Node + Postgres | 250 MB | Analytics, tight fit |
| Uptime Kuma | Node + SQLite | 120 MB | Disable Chromium checks |
| Caddy + static site | Go | 30 MB | Honestly use a CDN |
| WireGuard | kernel | <5 MB | Free VPN headend |
You can pick 2-3 of those and run them on the same $6 droplet without issue. On a Hetzner CX22 with 4 GB, you can run almost the entire list at once.
When to step up
The honest tipping points where you should stop fighting and pay for more RAM:
- You need search beyond
LIKE '%foo%'. Step up to 2 GB and run Meilisearch. - You need Postgres with more than ~50 MB
shared_buffers. Step up. - You want any kind of background worker pool (Sidekiq, RQ, Celery with prefork). Step up.
- You need to run Node and Postgres and Redis and an app server. Step up.
The next tier up — $12/mo at DO, $12 at Linode, ~€7-8 at Hetzner for a CX32 — typically gives you 2x RAM. That single jump unlocks roughly 80% of the "real" self-hosted app catalog (Nextcloud, Mastodon-lite, full Gitea with Actions, Umami at scale).
Decision checklist
Before you click "create droplet," answer these in order:
- Does the app have a SQLite mode? If yes, use it. If no, can you tolerate Postgres with
shared_buffers=32MB? - Is the runtime Go, Rust, or PHP-FPM? If it's Ruby, Java, or "Docker Compose with 6 services," you are on the wrong tier.
- Did you create a 2 GB swap file and set swappiness to 10? If no, do that before installing anything.
- Did you enable OPcache (PHP) or the equivalent compile cache for your runtime? Free 2-3x throughput.
- Is your egress under 1 TB/mo? If not, you are already on Hetzner whether you knew it or not — DO and Linode bill ~$0.01/GB over.
- Did you check the osscostcalc comparison tool for the exact app you want to run? The per-app RAM and egress estimates there will tell you within a minute whether the cheap tier is realistic or wishful thinking.
Run through that list and the $5 droplet stops being a toy. It becomes the most boring, most reliable piece of infrastructure you own.