Hello, tech enthusiasts! ๐
You built a small Python tool. Maybe it shows a sales summary, or it lets your team look up invoice status. It works great on your laptop. But when your laptop is off, nobody can use it.
The answer is a VPS, a Virtual Private Server. Think of it as renting a small computer in a data centre. It is always on and always online, and small ones cost only a few dollars a month.
Today we will rent one, connect safely, and put a tiny Flask app online.
What you need
- A small VPS with Ubuntu 24.04 (1 CPU and 1 GB of memory is enough)
- A terminal on your computer (Windows 10 and 11 have
sshbuilt in) - Optional: a domain name, like example.com
Step 1: Rent the server and connect with SSH keys
Pick any hosting provider and create the smallest Ubuntu server. You will get its IP address.
SSH is a safe tunnel to type commands on the server. Instead of a password, we use an SSH key. A key is like a house key that only you have: much harder to guess than a password.
On your own computer, create a key:
ssh-keygen -t ed25519
Most providers let you paste your public key (the file ending in .pub) when you create the server. Do that. Then connect (replace the address with your server’s IP):
ssh root@203.0.113.10
Step 2: Update and create a normal user
First, install all updates:
apt update && apt upgrade -y
Working as root all the time is risky. Create a normal user with admin rights and copy your key to it:
adduser deploy
usermod -aG sudo deploy
rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy
Log out and connect again as ssh deploy@203.0.113.10. Once that works, turn off password logins. Open /etc/ssh/sshd_config, set PasswordAuthentication no, check that no file in /etc/ssh/sshd_config.d/ sets it back to yes, then run sudo systemctl restart ssh.
Step 3: Create the Python app
Install Python tools and nginx:
sudo apt install -y python3-venv nginx
Make a folder and a virtual environment. A venv is a private box for your app’s libraries, so they do not mix with the system.
mkdir ~/myapp && cd ~/myapp
python3 -m venv venv
source venv/bin/activate
pip install flask gunicorn
Create app.py:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def home():
return "Hello from my first server!"
Step 4: Run it with gunicorn and systemd
Flask’s built-in server is only for testing. Gunicorn is a real web server for Python apps. Test it:
gunicorn --workers 2 --bind 127.0.0.1:8000 app:app
Press Ctrl+C to stop. Now we want it to start by itself, even after a reboot. That is the job of systemd, Linux’s service manager. Create /etc/systemd/system/myapp.service with sudo nano:
[Unit]
Description=My Flask app
After=network.target
[Service]
User=deploy
WorkingDirectory=/home/deploy/myapp
ExecStart=/home/deploy/myapp/venv/bin/gunicorn --workers 2 --bind 127.0.0.1:8000 app:app
Restart=always
[Install]
WantedBy=multi-user.target
Start it and enable it at boot:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp
sudo systemctl status myapp
Step 5: Put nginx in front
Nginx is like a receptionist. It receives visitors on the normal web port (80) and passes them to gunicorn. It is fast and safe. Create /etc/nginx/sites-available/myapp:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
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;
}
}
Enable it, remove the default site, test and reload:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx
Step 6: Turn on the firewall
A firewall is a door guard. It only lets in the traffic you allow. Allow SSH first, so you do not lock yourself out:
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status
Now open http://203.0.113.10 (or your domain) in a browser. You should see your message.
Step 7: Add HTTPS
If you have a domain pointing to your server, get a free certificate with certbot:
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com
Certbot changes the nginx file for you and renews the certificate automatically.
Common problems
- 502 Bad Gateway โ Gunicorn is not running. Check
sudo systemctl status myappandjournalctl -u myapp. - Locked out after enabling ufw โ Use your provider’s web console, then run
sudo ufw allow 22/tcp. - Code changes do not show โ Restart the app with
sudo systemctl restart myapp. - certbot fails โ Your domain’s DNS must point to the server’s IP, and port 80 must be open.
Wrap-up
You now have a real server, with key-only SSH, a firewall, a proper app server and a web server in front. That is the same basic setup many big websites use.
Next idea: turn on automatic security updates with sudo apt install unattended-upgrades, so your server stays patched while you sleep.