Let’s capture this pattern as a template to re-use whenever Camelot needs a new service (music, wiki, file manager, API gateway, etc.).
Sometimes Django is better.... This might be one of those times....
Goal: deploy a self-contained Flask service on a Linux host with no Docker or cloud dependencies.
1️⃣ Project layout
/srv/music_service/
│
├── venv/ # Python virtual environment
├── app.py # Flask app entry point
├── requirements.txt
├── service.ini # uWSGI or gunicorn config (optional)
└── music_service.service # systemd unit file
2️⃣ Flask app (app.py)
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/api/ping")
def ping():
return jsonify(status="ok", service="music", message="Camelot hears the beat 🎵")
if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000)
3️⃣ Virtual environment
# cd /srv/music_service // prod env
cd /home/tux/music_service
python3 -m venv venv
source venv/bin/activate
pip install flask gunicornJust run it
cd music_service
python3 app.py
tux@localhost:~/music_service$ ll
total 4
-rw-r--r--. 1 tux tux 192 Nov 1 23:59 app.py
drwxr-xr-x. 6 tux tux 105 Nov 2 00:23 venv
tux@localhost:~/music_service$ source venv/bin/activate
(venv) tux@localhost:~/music_service$ python3 app.py
* Serving Flask app 'app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:7001
Press CTRL+C to quit
127.0.0.1 - - [02/Nov/2025 01:27:38] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [02/Nov/2025 01:27:38] "GET /favicon.ico HTTP/1.1" 404 -
4️⃣ systemd unit (/etc/systemd/system/music_service.service)
[Unit]
Description=Music Service (Flask)
After=network.target
[Service]
User=camelot
Group=camelot
WorkingDirectory=/home/tux/music_service
Environment="PATH=/home/tux/music_service/venv/bin"
ExecStart=/home/tux/music_service/venv/bin/gunicorn -b 127.0.0.1:5000 app:app
Restart=always
[Install]
WantedBy=multi-user.targetEnable and start it:
sudo systemctl enable --now music_service
5️⃣ Nginx reverse proxy (/etc/nginx/conf.d/music_service.conf)
server {
listen 80;
server_name camelot.local;
location /music/ {
proxy_pass http://127.0.0.1:5000/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}Reload Nginx:
sudo nginx -t && sudo systemctl reload nginx🏗 Philosophy of the Pattern
|
Component |
Role |
|---|---|
|
Flask |
Simple, readable API surface — “speak JSON, not jargon.” |
|
venv |
Isolation: each service owns its Python world. |
|
systemd |
Stability: runs at boot, restarts if it crashes. |
|
Nginx |
Integration: unifies multiple services under one domain. |
This pattern scales horizontally — every service you create (wiki, music, transfer depot, etc.) can use the same bones. You just swap out the content logic.
Perfect — that means Jupyter’s already wired to your system Python, and your virtual environment (venv) is active. 🧙♂️✨
So right now you’ve got:
- ✅ Python kernel (system-wide)
-
✅ Active venv for your project (
~/music_service/venv)
Let’s make the venv appear as its own selectable kernel inside Jupyter — so you can open notebooks in that exact environment (with Flask, your music modules, etc.).
🪄 Add your venv to Jupyter
Inside the venv:
pip install ipykernel
python -m ipykernel install --user --name=music_service --display-name "Python (music_service)"You’ll see:
Installed kernelspec music_service in /home/tux/.local/share/jupyter/kernels/music_serviceNow when you run:
jupyter notebookand click “New,” you’ll have a second kernel option:
Python (music_service)
That’s your isolated environment — whatever packages you pip install in the venv will be available there too.
🎶 Try it out
In a new notebook cell:
from flask import Flask
print("Flask ready in Camelot’s music service env 🎵")If it runs without ImportError, everything’s golden.
Would you like me to give you a tiny Jupyter notebook cell that turns your music_tags dictionary into a pretty, expandable tree (so you can browse music eras and genres interactively)?