DigitalOcean 2 GB — the floor
The whole product (API + Postgres + storefront + admin + Caddy) on the smallest supported box, no Redis/Celery. This is the official minimum spec, and the measured verdict is honest: supported, with the swap caveat.
- Target: DigitalOcean Basic droplet, 2 GB RAM / 1 vCPU / 50 GB SSD (~$12/mo; the 2 GB / 2 vCPU option works the same).
- Profile: lite.
- Time: ~30 minutes. You need: this repo, a domain, three DNS records.
Get the server: Get your DigitalOcean Droplets here.
-
Create the droplet.
Open DigitalOcean → Create → Droplet:
- Image: Ubuntu 24.04 LTS
- Plan: Basic → Regular → 2 GB RAM / 1 vCPU (or 2 GB / 2 vCPU)
- Authentication: an SSH key if you have one (recommended — the hardening guide shows how to create one), otherwise a strong root password.
Create it, wait for it to boot, and note the droplet’s IP address.
-
Point your domain at the droplet.
Add three A records at your DNS provider —
@(storefront),api(the API) andadmin(staff admin app), each with value = the droplet IP, TTL 300. Verify withping your-domain.comandping api.your-domain.com. Do this before first boot so the HTTPS certificates can be issued automatically. -
Log in, add swap, install Docker.
Terminal window ssh root@YOUR_DROPLET_IPSwap first. At the 2 GB floor, swap protects against a one-off spike (a huge import, a burst of image processing) turning into an OOM kill. The stack doesn’t live in swap — it just makes the worst case degrade gently:
Terminal window fallocate -l 2G /swapfilechmod 600 /swapfilemkswap /swapfileswapon /swapfileecho '/swapfile none swap sw 0 0' >> /etc/fstabsysctl vm.swappiness=10 && echo 'vm.swappiness=10' >> /etc/sysctl.conffree -h # should now show 2.0Gi swap(
swappiness=10means “use swap only under pressure”, so normal traffic stays in RAM.) Then Docker:Terminal window apt-get update && apt-get -y upgradecurl -fsSL https://get.docker.com | shdocker --version && docker compose version -
Get the code.
Terminal window apt-get -y install gitgit clone YOUR_REPO_URL /srv/appcd /srv/app(Or upload your purchase zip — the Hostinger guide, step 4 shows the
scp+unzipvariant; the steps are identical.) -
Configure
.env.Terminal window cp .env.example .envnano .envMinimum edits (same as the Hostinger guide):
DJANGO_SECRET_KEY— generate:docker run --rm python:3.13-slim python -c "import secrets; print(secrets.token_urlsafe(64))"DOMAIN=your-domain.com— the shop domain (theapi./admin.hosts, allowed hosts, CORS and CSRF derive from it; for the split topology, set the API host instead)DB_PASSWORD— a strong passwordDJANGO_SUPERUSER_USERNAME/_EMAIL/_PASSWORD— your admin login
Keep
INFRA_PROFILE=lite(the default) — that’s the whole point of this box. Save (Ctrl+O,Enter) and exit (Ctrl+X). -
Launch (lite is the default compose file).
Terminal window docker compose up -dFirst run builds the image (5–10 minutes on 1 vCPU — be patient). Boot is fully automatic: wait-for-db → migrate → collectstatic → superuser → serve.
Terminal window docker compose ps # wait for web "(healthy)"docker compose logs -f web # Ctrl+C to stopYou’ll see seven containers:
caddy(the only one with public ports),web,postgres,scheduler(cron jobs),backup(nightly database dump, 7-day rotation), plus the two prebuilt Next.js appsstorefrontandadmin. Every one runs under a hard memory limit totalling ~1.9 GB — tight on 2 GB, which is exactly why step 3’s swap is mandatory here. -
Verify.
Terminal window curl -s https://api.your-domain.com/health/ # → "status": "success"free -h # see the actual usagedocker stats --no-stream # per-container memoryOpen
https://api.your-domain.com/admin/and log in.
2 GB alternative: split topology
Section titled “2 GB alternative: split topology”Prefer real headroom at 2 GB? Keep only the API + database on this small box and host the two Next.js apps on a free/cheap frontend platform (they’re standard Next.js apps):
docker compose -f docker-compose.yml -f docker-compose.api-only.yml up -dThis scales the storefront / admin containers to 0 and swaps Caddy to the
single-domain layout (DOMAIN serves the API directly). Point the hosted apps at
the box via their own runtime env (API_URL / API_PUBLIC_URL) and add their
origins to DJANGO_TRUSTED_CORS_ORIGINS / DJANGO_CSRF_TRUSTED_ORIGINS. With
this layout you only need the api DNS record on the droplet.
Living at the floor: what to know
Section titled “Living at the floor: what to know”- Email/SMS are sent inline with strict 2-second timeouts in lite — configure
a real SMTP provider in
.env(DJANGO_EMAIL_*); failures are retried automatically every 10 minutes from the outbox. - Heavy image uploads are bounded (10 MB / 40 MP — oversized uploads get a clean error instead of eating your RAM).
- If you outgrow the box (sustained traffic, real-time needs), don’t tune — move up: an 8 GB VPS + the full profile is a one-flag change.
If something doesn’t work
Section titled “If something doesn’t work”The Hostinger troubleshooting table
applies here too, plus one floor-specific check: if a container was killed,
docker inspect <name> --format '{{.State.OOMKilled}}' tells you if it hit its
memory limit. With swap enabled and stock settings, this should never happen —
it’s exactly what the shipped resource budget
verifies.