Deploy FastAPI and React on Hostinger VPS with Namecheap DNS

by Fahim

Serverless platforms make deploying a full-stack app feel effortless until the first traffic spike hits your monthly bill. Moving your FastAPI backend and React frontend onto an Ubuntu VPS on Hostinger gives you predictable pricing, dedicated compute, and total control over your stack.

Here is the exact setup I use in production: Nginx serves the built React static files, proxies /api requests to a Gunicorn-managed Uvicorn process managed by Systemd, Let’s Encrypt handles SSL, and Namecheap routes DNS with proper edge caching headers.

Deploy FastAPI and React on Hostinger VPS with Namecheap DNS
Deploy FastAPI and React on Hostinger VPS with Namecheap DNS

1. Provision and Harden the Hostinger VPS

Spin up a fresh Ubuntu 22.04 or 24.04 VPS instance on Hostinger. Grab the public IP from hPanel, SSH in as root, update your packages, and create a non-root administrative user immediately so you’re not running builds as root.

Run these commands to set up the deploy user and permissions:

apt update && apt upgrade -y
adduser deploy
usermod -aG sudo deploy
rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy

Lock down UFW so only SSH, HTTP, and HTTPS traffic can reach the box. Never leave raw backend ports like 8000 exposed to the public internet.

ufw default deny incoming
ufw default allow outgoing
ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw --force enable

Switch over to the new deploy user before installing runtimes:

su - deploy

2. Install Python, Node.js, and Build Tools

The backend needs Python 3.10+ with venv, while the React frontend needs Node.js to compile your production assets. We’ll pull Python from Ubuntu’s repos and Node from NodeSource.

sudo apt install -y python3-pip python3-venv nginx git curl
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs

Check that both runtimes installed cleanly:

python3 --version
node -v
npm -v

If you’re running companion Node microservices next to Python, check out our walkthrough on how to deploy Node.js on Hostinger VPS with Nginx for process management tips.

3. Set Up the FastAPI Backend and Systemd Service

Keep your codebase organized under /var/www/app. Create the directories and hand ownership to deploy.

sudo mkdir -p /var/www/app
sudo chown -R deploy:deploy /var/www/app
cd /var/www/app
mkdir backend frontend

Drop into /var/www/app/backend, set up a dedicated virtual environment, and install your dependencies.

cd /var/www/app/backend
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install fastapi uvicorn[standard] pydantic gunicorn

Add a quick sanity-check script in main.py:

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware app = FastAPI(title="Production API") app.add_middleware( CORSMiddleware, allow_origins=["https://example.com", "https://www.example.com"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"],
) @app.get("/api/health")
def health_check(): return {"status": "healthy", "environment": "production"} @app.get("/api/data")
def get_sample_data(): return {"items": ["PostgreSQL", "Redis", "Nginx", "FastAPI"]}

Test run it on localhost port 8000:

uvicorn main:app --host 127.0.0.1 --port 8000

Kill the test with Ctrl+C once it works. Now, write a Systemd unit file so Gunicorn restarts automatically on crashes or reboots.

[Unit]
Description=FastAPI Uvicorn Production Daemon
After=network.target
[Service]
User=deploy
Group=deploy
WorkingDirectory=/var/www/app/backend
Environment="PATH=/var/www/app/backend/venv/bin"
ExecStart=/var/www/app/backend/venv/bin/gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker -b 127.0.0.1:8000
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target

Save that file to /etc/systemd/system/fastapi.service, reload the daemon, and fire it up:

sudo systemctl daemon-reload
sudo systemctl start fastapi
sudo systemctl enable fastapi
sudo systemctl status fastapi

4. Build the React Frontend

Head to your frontend directory and drop in your React project. I usually stick with Vite + React for lightweight, fast production builds.

cd /var/www/app/frontend

Make sure your frontend API calls use relative paths (like fetch(‘/api/data’)) instead of hardcoded http://localhost:8000 URLs. Nginx will route them under the hood.

Build the static bundle into the dist folder:

npm install
npm run build

Your static files land in /var/www/app/frontend/dist. Make sure Nginx’s worker user can read the files:

sudo chmod -R 755 /var/www/app/frontend/dist

5. Point Namecheap DNS to Your VPS

Head over to your Namecheap Dashboard, click Manage next to your domain, and open the Advanced DNS tab.

  • Add an A Record: Host @, Value = your Hostinger VPS IP address, TTL = Automatic.
  • Add a CNAME Record: Host www, Value = your root domain (e.g., example.com.).

If you need custom apex nameservers or complex DNS setups, see our complete guide on how to create custom nameservers and glue records in Namecheap.

6. Set Up Nginx Reverse Proxy and SSL

We want Nginx handling two jobs: serving static React files on root requests (/) and reverse proxying any /api/ calls straight to Uvicorn on 127.0.0.1:8000.

Create your site config at /etc/nginx/sites-available/app.conf:

server { listen 80; server_name example.com www.example.com; root /var/www/app/frontend/dist; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }
}

Symlink it to sites-enabled and test the syntax:

sudo ln -s /etc/nginx/sites-available/app.conf /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl restart nginx

Grab free SSL certificates using Certbot through Let’s Encrypt:

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com

If Certbot fails on domain challenge verification, read through our guide to fix Certbot ACME HTTP-01 challenge failed errors on Nginx.

7. Configure CDN Caching and Static Headers

When placing a CDN in front of your React build, you want long cache headers on hashed assets in /assets/ and zero cache on index.html so users get fresh releases immediately.

Update /etc/nginx/sites-available/app.conf with explicit caching directives:

location /assets/ { expires 1y; add_header Cache-Control "public, max-age=31536000, immutable"; access_log off;
} location = /index.html { expires -1; add_header Cache-Control "no-store, no-cache, must-revalidate";
}

For a deeper dive into header rules and CDN behaviors, check out our guide on CORS and Cache-Control headers for CDN static assets.

8. Common Gotchas and Troubleshooting

Here are the three errors that catch most people during deployment:

  • 502 Bad Gateway: Nginx can’t reach port 8000. Check the service status with sudo systemctl status fastapi, or tail the crash logs via journalctl -u fastapi -n 50 --no-pager.
  • CORS errors in browser console: Double-check that your FastAPI allow_origins array matches your exact domain, including https://.
  • 404 on page refresh in React: You forgot try_files $uri $uri/ /index.html; in your Nginx root location block. Without it, Nginx looks for a real directory instead of letting React Router handle the route.

Frequently Asked Questions

Why use Gunicorn with Uvicorn workers instead of plain Uvicorn?

Uvicorn is an ASGI server, but it isn’t built to be a resilient process manager. Running Gunicorn with uvicorn.workers.UvicornWorker gives you multi-core worker management, graceful worker restarts, and crash recovery out of the box.

How do I deploy updates to the React frontend?

Pull your latest code into /var/www/app/frontend and run npm run build. Nginx reads straight from dist, so changes go live the second the build finishes.

How do I deploy backend updates without downtime?

Pull your new backend code, activate the virtualenv to run any database migrations, and reload the service with sudo systemctl reload fastapi. Gunicorn handles the rolling worker restart with zero dropped requests.

all_in_one_marketing_tool