Skip to main content
← All guides

Deploying your own web app to a server (Node.js, Python)

This is the whole process for taking an Express/Next.js (Node.js) or Flask/FastAPI (Python) app you built on your PC, running it 24/7 on the server, and making it reachable through your domain. Based on Ubuntu.

The overall flow

  1. Get the code onto the server (GitHub)
  2. Put secrets in .env
  3. Keep the app running (pm2 for Node.js, systemd for Python)
  4. Connect your domain with Nginx and add HTTPS

1. Get the code

If your GitHub repository is public, clone it directly.

git clone https://github.com/myid/myapp.git
cd myapp

For a private repository (deploy key)

If you create a key just for the server and register it on the repository as "read-only", you don't have to keep your GitHub password on the server.

ssh-keygen -t ed25519 -f ~/.ssh/deploy_key -N ""
cat ~/.ssh/deploy_key.pub

Copy the line that's printed and paste it into your GitHub repository → Settings → Deploy keys → Add deploy key. (Don't check "Allow write access".) Then set up the server to use this key, and clone.

printf 'Host github.com\n  IdentityFile ~/.ssh/deploy_key\n' >> ~/.ssh/config
git clone [email protected]:myid/myapp.git

2. Secrets go in .env

Don't write values like DB passwords and API keys directly in your code; put them in a .env file on the server.

nano .env

🔒 Add .env to .gitignore so it doesn't get uploaded to GitHub.

3-1. Node.js app (pm2)

Installing Node.js is the same as "Install Node.js" in Hosting a Discord bot 24/7 (discord.js, discord.py).

npm ci                                   # Install based on package-lock.json
npm run build                            # Only for apps with a build step (Next.js, etc.)
sudo npm install -g pm2
pm2 start npm --name my-app -- start     # Run with the start script in package.json
pm2 save
pm2 startup                              # Copy the command it prints and run that too

3-2. Python app (systemd)

sudo apt install -y python3-venv
python3 -m venv venv
./venv/bin/pip install -r requirements.txt
./venv/bin/pip install uvicorn       # For FastAPI
./venv/bin/pip install gunicorn      # For Flask

Create a service file.

sudo nano /etc/systemd/system/my-app.service

Replace username and myapp in the paths with your actual values, and keep only the one ExecStart line that fits your app. (main:app means app inside main.py; app:app means app inside app.py.)

[Unit]
Description=My Web App
After=network-online.target

[Service]
User=username
WorkingDirectory=/home/username/myapp
EnvironmentFile=/home/username/myapp/.env
# FastAPI
ExecStart=/home/username/myapp/venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000
# Flask
# ExecStart=/home/username/myapp/venv/bin/gunicorn -w 2 -b 127.0.0.1:8000 app:app
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now my-app
sudo systemctl status my-app      # Success if it shows active (running)

4. Domain and HTTPS

Set up Nginx, your domain and a certificate by following Putting up a website, then set the proxy_pass port in the "If you want to connect a Node.js or Python app" section to your app's port (usually 3000 for Node.js; 8000 in the Python example above).

You don't need to open the app ports (3000, 8000) in the firewall. It's safer to open only Nginx's 80 and 443 to the outside and let the app be reached only through Nginx.

5. Updating after you change your code

cd ~/myapp
git pull

# Node.js
npm ci && npm run build && pm2 restart my-app

# Python
./venv/bin/pip install -r requirements.txt && sudo systemctl restart my-app

If it doesn't work

  • Check the logs first — pm2 logs my-app or journalctl -u my-app -f
  • 502 Bad Gateway — The app is down, or Nginx's proxy_pass port is different from the app's port.
  • EADDRINUSE / Address already in use — Another program is already using that port. Check what's using it with sudo ss -ltnp | grep 3000.
  • The build stalls because memory runs out — If the server freezes during npm run build, try adding swap.

If your problem isn't solved, open a ticket on our Discord.