Chapters: 

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 gunicorn

 

Just 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.target

Enable 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_service

Now when you run:

jupyter notebook

and 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)?