Skip to content

Hostinger KVM 2 — the flagship path

The whole product on one box: storefront + staff admin app + API + database behind Caddy with automatic HTTPS. This is the flagship path — the full profile (Redis + Celery + websockets), measured comfortable at this tier.

  • Target: Hostinger KVM 2 — 2 vCPU, 8 GB RAM, 100 GB NVMe (~$7–9/mo).
  • Time: ~30–40 minutes, most of it waiting for installs.
  • You need: this repo (your purchase download or git access), a domain name, and the ability to add three DNS records.

Get the server: Hostinger KVM 2 VPS

  1. Buy the VPS and pick the OS.

    Open Hostinger KVM 2 and complete the purchase, then in the setup flow (hPanel → VPS → Setup):

    • Operating system: Ubuntu 24.04 LTS — the plain OS, not a template with a control panel.
    • Set a strong root password, and add your SSH key if you have one (you can add it later — the hardening guide shows how to make one).

    Wait for the VPS to reach Running, then note its IP address from the dashboard.

  2. Point your domain at the server.

    The box serves the whole product on three hostnames, so add three A records at your DNS provider:

    Type Name Value TTL Serves
    A @ (bare domain, or a shop subdomain) your VPS IP 300 storefront
    A api your VPS IP 300 Django API
    A admin your VPS IP 300 staff admin app

    We’ll use your-domain.com in the examples — substitute yours everywhere. DNS usually propagates in minutes; check with ping your-domain.com and ping api.your-domain.com until they answer with the VPS IP.

  3. Log in and install Docker.

    From your laptop’s terminal (Windows: PowerShell or PuTTY):

    Terminal window
    ssh root@YOUR_VPS_IP

    On the server:

    Terminal window
    apt-get update && apt-get -y upgrade
    curl -fsSL https://get.docker.com | sh
    docker --version && docker compose version # both should print versions
  4. Get the code.

    Clone your repository:

    Terminal window
    apt-get -y install git
    git clone YOUR_REPO_URL /srv/app
    cd /srv/app

    …or upload your purchase zip and unpack it:

    Terminal window
    # from your laptop:
    scp product.zip root@YOUR_VPS_IP:/srv/
    # on the server:
    apt-get -y install unzip && mkdir -p /srv/app && unzip /srv/product.zip -d /srv/app && cd /srv/app
  5. Configure .env.

    Terminal window
    cp .env.example .env
    nano .env

    Change at least these values (search with Ctrl+W in nano):

    Variable Set it to
    DJANGO_SECRET_KEY a fresh random key — generate on the server: docker run --rm python:3.13-slim python -c "import secrets; print(secrets.token_urlsafe(64))"
    DOMAIN your-domain.com — the shop domain; the api. and admin. hosts, allowed hosts, CORS and CSRF all derive from it automatically
    DB_PASSWORD a strong database password
    DJANGO_SUPERUSER_USERNAME / _EMAIL / _PASSWORD your admin login (created automatically on first boot)

    Optional now, easy later (all runtime — see Branding & domains): APP_NAME, BRAND_ACCENT / BRAND_LOGO_URL, provider keys for payments / SMS / couriers (the .env comments name each), and ADMIN_ENABLED_DOMAINS. Brand and theme are best set in the tenant config after first boot.

    Leave DJANGO_ENV=production and DJANGO_DEBUG=False as they are. Save and exit: Ctrl+O, Enter, Ctrl+X.

  6. Launch (full profile).

    Terminal window
    docker compose -f docker-compose.yml -f docker-compose.full.yml up -d

    First run builds the API image, pulls the prebuilt storefront + admin images, and starts everything — expect 5–10 minutes. The web container waits for the database, migrates, collects static files and creates your superuser by itself; there are no manual steps. The two Next.js apps are configured entirely by .env at start-up — never rebuilt.

    Watch it come up:

    Terminal window
    docker compose logs -f web # Ctrl+C to stop watching
    docker compose ps # wait for web: "(healthy)"
  7. Verify.

    Terminal window
    curl -s https://api.your-domain.com/health/ # API
    curl -s https://your-domain.com/api/config # storefront runtime config
    curl -s https://admin.your-domain.com/api/config # admin runtime config

    The first shows "status": "success"; the other two return the public runtime config each app derived from your .env — all over real Let’s Encrypt certificates Caddy obtained automatically. Then open your shop (https://your-domain.com), the staff app (https://admin.your-domain.com), and the deep-ops console (https://api.your-domain.com/admin/; log in with the superuser from .env).

Symptom Likely cause / fix
Browser warns about the certificate DNS wasn’t pointing at the VPS when Caddy started. Fix the A record, then docker compose restart caddy.
curl: connection refused Ports blocked — check docker compose ps shows caddy Up, and no firewall is blocking 80/443 (see hardening; Hostinger’s hPanel firewall must also allow 80/443).
DisallowedHost error page Your domain isn’t in DJANGO_ALLOWED_HOSTS → edit .env and docker compose up -d web.
web container restarts repeatedly docker compose logs web — most often a missing/weak DJANGO_SECRET_KEY (production refuses keys under 50 chars) or a DB password mismatch after an earlier attempt (docker compose down -v wipes the old database volume, then up -d again — only safe before you have real data).
Want to seed a demo catalog to click around docker compose exec web python manage.py seed_client --profile belltex --demo