What this is
This guide walks through building a small self-hosted server for useful tools and downloads. The goal is not to build a giant platform. The goal is to build something simple, private, and dependable that you can actually understand and maintain.
What you need
- Raspberry Pi 4 with a decent power supply
- Raspberry Pi OS Lite 64-bit
- Python 3, nginx, and a network connection
- A domain name if you want outside access
- Some patience, because computers still enjoy nonsense
1. Install the base system
Flash Raspberry Pi OS Lite, boot the Pi, and make sure SSH is enabled. Then update the system.
sudo apt update && sudo apt upgrade -y
2. Install the packages you actually need
Keep it simple. For a Flask-based site, this is enough to get moving.
sudo apt install python3 python3-venv python3-pip nginx -y
3. Create your project directory
Put your site in its own folder so it stays predictable and easy to back up.
mkdir ~/thehermit-site cd ~/thehermit-site python3 -m venv venv source venv/bin/activate
4. Install your Python packages
pip install flask gunicorn flask-login flask-limiter itsdangerous
5. Build the app
Start with a basic Flask app, then add your routes, templates, downloads, and account features as needed. Do not overbuild it on day one. Useful and stable beats clever and broken.
6. Run it with Gunicorn
Do not leave the Flask development server running for real use.
gunicorn --workers 2 --bind 127.0.0.1:5000 app:app
7. Create a systemd service
This keeps the app running on boot and makes restarting it easy.
[Unit] Description=The Hermit Site After=network.target [Service] User=devin WorkingDirectory=/home/devin/thehermit-site ExecStart=/home/devin/thehermit-site/venv/bin/gunicorn --workers 2 --bind 127.0.0.1:5000 app:app EnvironmentFile=/etc/thehermit-site.env [Install] WantedBy=multi-user.target
sudo systemctl daemon-reload sudo systemctl enable thehermit-site sudo systemctl start thehermit-site sudo systemctl status thehermit-site
8. Put nginx in front of it
nginx listens on the normal web ports and passes requests to Gunicorn.
location / {
proxy_pass http://127.0.0.1:5000;
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;
}
sudo nginx -t sudo systemctl restart nginx
9. Add a downloads folder
If you want to serve APKs, ZIP files, or other downloads, keep them in one dedicated location.
mkdir /home/devin/thehermit-site/private_downloads
Then serve them through Flask with a controlled route instead of exposing random folders.
from flask import send_from_directory, abort
import os
PRIVATE_DOWNLOADS = "/home/devin/thehermit-site/private_downloads"
@app.route("/downloads/<path:filename>")
@login_required
def download_file(filename):
safe_path = os.path.abspath(os.path.join(PRIVATE_DOWNLOADS, filename))
private_root = os.path.abspath(PRIVATE_DOWNLOADS)
if not safe_path.startswith(private_root):
abort(403)
if not os.path.isfile(safe_path):
abort(404)
return send_from_directory(PRIVATE_DOWNLOADS, filename, as_attachment=True)
10. Keep secrets out of your code
Put your secret key, base URL, and mail credentials in an environment file instead of hardcoding them.
sudo nano /etc/thehermit-site.env
SECRET_KEY=replace-this BASE_URL=https://yourdomain.com SESSION_COOKIE_SECURE=true SMTP2GO_HOST=mail.smtp2go.com SMTP2GO_PORT=587 SMTP2GO_USER=your-user SMTP2GO_PASS=your-pass [email protected]
11. Use a tunnel instead of opening router ports
A Cloudflare Tunnel is a clean way to expose your site without dealing with direct port forwarding. It is usually less annoying than fighting with consumer routers, which is not a high bar, but still.
12. Back it up
At minimum, back up your project folder, templates, environment notes, and database. A working server with no backup is just a future headache waiting for its cue.
zip -r thehermit-site-backup.zip /home/devin/thehermit-site
Final thoughts
A self-hosted tool server does not need to be huge to be valuable. If it runs your tools, hosts your files, and stays under your control, it is doing the job. Start simple, keep it understandable, and add features only when they solve a real problem.