Introduction
At some point a side project outgrows your laptop. You want it online, running around the clock, at a domain you own. The usual answer is a VPS running Debian: a small rented server with a famously stable operating system. It's cheap, it teaches you a lot, and it gives you complete control over where your app lives.
The Problem a VPS Solves
There are plenty of ways to host a site, and each makes a trade-off:
- Shared hosting โ inexpensive and simple, but you can't install what you need or run your own processes
- Platform-as-a-service โ convenient, but pricing grows quickly and you're tied to the platform's rules
- Your own hardware โ total control, but you also own the power, the network, and the 3 a.m. outages
A VPS sits in the middle. You get a real server with root access, a fixed monthly price, and none of the hardware headaches.
The VPS + Debian Approach
A VPS (Virtual Private Server) is a slice of a physical machine that behaves like a computer of its own. You pick the size, install what you want, and nobody else's workload shares your operating system.
Debian is a popular choice for that operating system because it prioritizes stability. Packages in the stable release are tested thoroughly, security fixes arrive steadily, and a release stays supported for years. For a server you want to set up once and trust, "boring" is a feature.
Core idea: Rent a small server, lock it down, and run your app as a handful of well-understood services.
Simple Example
After your provider creates the server, you connect over SSH:
ssh root@your-server-ip
The first thing to do is update everything:
apt update && apt upgrade -y
Within a minute, you're working on a real Debian machine on the internet.
Setting Up a Server for a Django Blog
Here is a sensible path from a blank server to a live site. Each step is small on its own.
1. Create a Non-Root User
Running everything as root is risky, since one mistake or compromise has no guardrails. Create a regular user with sudo rights:
apt install -y sudo
adduser deploy
usermod -aG sudo deploy
2. Use SSH Keys and Disable Passwords
From your own computer, copy your public key to the server:
ssh-copy-id deploy@your-server-ip
Then on the server, edit /etc/ssh/sshd_config and set:
PermitRootLogin no
PasswordAuthentication no
Apply the change:
sudo systemctl reload ssh
Important: keep your current session open and test a new login as deploy in a second terminal before you log out. If something is wrong, you still have a way back in.
3. Turn On a Firewall
Only expose what you need: SSH for you, and web traffic for visitors.
sudo apt install -y ufw
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
If you changed your SSH port, allow that one instead of 22 before enabling the firewall.
4. Enable Automatic Security Updates
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
This installs security patches in the background, which is one of the best returns on effort for a server you don't watch daily.
5. Run the App with Gunicorn and systemd
systemd keeps your app running, restarts it if it crashes, and starts it at boot. Create /etc/systemd/system/blog.service:
[Unit]
Description=Gunicorn for the blog
After=network.target
[Service]
User=deploy
WorkingDirectory=/srv/blog
EnvironmentFile=/srv/blog/.env
ExecStart=/srv/blog/.venv/bin/gunicorn config.wsgi:application --bind 127.0.0.1:8000
Restart=on-failure
[Install]
WantedBy=multi-user.target
Then enable and start it:
sudo systemctl daemon-reload
sudo systemctl enable --now blog
sudo systemctl status blog
Adjust the paths and the config.wsgi module name to match your project.
6. Put Nginx in Front
Gunicorn shouldn't face the internet directly. Nginx handles incoming connections, serves static files efficiently, and passes everything else to your app.
sudo apt install -y nginx
A minimal site config in /etc/nginx/sites-available/blog:
server {
listen 80;
server_name example.com www.example.com;
location /static/ {
alias /srv/blog/staticfiles/;
}
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-Proto $scheme;
}
}
Enable it and reload:
sudo ln -s /etc/nginx/sites-available/blog /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Because Nginx terminates HTTPS, tell Django to trust the forwarded header in your settings:
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
7. Add HTTPS
Point your domain's DNS at the server, then let Let's Encrypt issue a free certificate:
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
Certbot edits your Nginx config and sets up automatic renewal.
Why This Matters
-
Full control โ Install any version of anything, run background workers, schedule jobs, and inspect problems directly.
-
Predictable cost โ A small VPS costs a fixed amount per month, whether you get ten visitors or ten thousand.
-
Lasting skills โ Linux, SSH, systemd, and Nginx knowledge transfers to almost every job and project you'll touch.
-
Stable foundation โ Debian's conservative release cycle means fewer surprises after an update.
Key Commands for Daily Life
| Command | Purpose | Example |
|---|---|---|
apt update && apt upgrade |
Install available updates | sudo apt update && sudo apt upgrade |
systemctl |
Manage services | sudo systemctl restart blog |
journalctl |
Read service logs | journalctl -u blog -f |
ufw status |
Check firewall rules | sudo ufw status verbose |
df -h |
Check disk space | df -h |
htop |
Watch CPU and memory | htop |
Real-World Use Cases
A small Debian VPS comfortably runs:
- Personal sites and blogs โ Django, static sites, or anything behind Nginx
- Self-hosted tools โ Analytics, RSS readers, password managers, and file sync
- Docker hosts โ A simple place to run containers with Compose
- Side projects and staging servers โ A real environment to test deployments
- Small APIs and bots โ Long-running processes that need to stay up
Getting Started
-
Choose a provider and a size. For a small blog, one CPU core and 1 to 2 GB of RAM is plenty.
-
Pick the current Debian stable image when creating the server.
-
Log in, update, and create your non-root user as shown above.
-
Lock down SSH and the firewall before deploying anything.
-
Deploy your app, put Nginx in front, and add HTTPS.
-
Set up backups. Copy your database and uploaded files somewhere off the server on a schedule. A server with no backups is a risk waiting to happen.
Know the Limits
Running your own server is rewarding, and it comes with responsibility:
- You are the sysadmin. Updates, security, and monitoring are your job.
- One server is one point of failure. If it goes down, your site goes down with it.
- Mistakes are easy. A bad firewall rule or SSH setting can lock you out, so change things carefully and keep a second session open.
- Your provider's console is your safety net. Know how to reach it before you need it.
The Philosophy
A VPS isn't the easiest way to host a site, and that's exactly why it's worth learning. It takes the mystery out of what "deploying" means: your code, a process manager, a web server, and a certificate. Once you've seen each piece, no platform you use afterwards will feel like a black box.
Start with one small server. Lock it down, automate the updates, and back it up. The result is a quiet, inexpensive machine that you understand completely.
This post is part of a series on the tools behind this blog: Django, HTMX, Tailwind CSS, PostgreSQL, SQLite, Docker, and now Debian on a VPS. Posts are authored in the Django admin as Markdown and rendered to HTML on save.