Chapters: 

Feb 15, 2026

Oh yes. This deserves a proper upgrade. ๐Ÿฐ๐Ÿ“œ
We know more now โ€” and we can make it clearer without bloating it.

Letโ€™s refine it so itโ€™s accurate, layered, and future-proof.


๐Ÿ”ฎ Virtualenv โ€” Clarified

Virtualenvs are not containers.
They do not isolate the OS, filesystem, users, or processes.

They:

  • Create an isolated Python interpreter context
  • Provide a separate site-packages
  • Modify $PATH when activated
  • Set $VIRTUAL_ENV
  • Allow dependency isolation per project

They are safe to use from:

  • Multiple terminals
  • Multiple SSH sessions
  • Concurrent shells

They do not spawn services.
They do not consume resources when idle.
They are just a different toolbench in the same room.


๐Ÿฐ Project Structure โ€” Updated View

(comics.eckford.ca)
โ”œโ”€โ”€ app.py                 โ† Main Flask application object (`app`)
โ”œโ”€โ”€ passenger_wsgi.py      โ† WSGI entrypoint used by Passenger
โ”œโ”€โ”€ restart.sh             โ† Custom restart trigger wrapper
โ”œโ”€โ”€ routes/                โ† Blueprints & API modules
โ”‚   โ”œโ”€โ”€ spells.py
โ”‚   โ””โ”€โ”€ note.py
โ”œโ”€โ”€ spells/                โ† Markdown content (rendered dynamically)
โ”‚   โ””โ”€โ”€ 00_spellbook.md
โ”œโ”€โ”€ static/                โ† CSS, JS, images
โ”œโ”€โ”€ templates/             โ† Jinja2 templates
โ”‚   โ”œโ”€โ”€ index.html
โ”‚   โ”œโ”€โ”€ hello.html
โ”‚   โ”œโ”€โ”€ markdown.html
โ”‚   โ””โ”€โ”€ note_ui.html
โ”œโ”€โ”€ tmp/
โ”‚   โ””โ”€โ”€ restart.txt        โ† Touch to signal Passenger reload
โ””โ”€โ”€ ~/virtualenv/comics.eckford.ca/3.8/
    โ”œโ”€โ”€ bin/python         โ† Interpreter used by this app
    โ”œโ”€โ”€ bin/pip            โ† Pip bound to this environment
    โ””โ”€โ”€ lib/python3.8/site-packages/
        โ”œโ”€โ”€ flask
        โ”œโ”€โ”€ flask_restful
        โ”œโ”€โ”€ markdown
        โ””โ”€โ”€ ...

๐Ÿง  Operational Notes

Activate Forge

source ~/virtualenv/comics.eckford.ca/3.8/bin/activate

Power Down Forge

deactivate

Restart Passenger (only needed for Python changes)

touch tmp/restart.txt

Template, CSS, and Markdown changes do not require restart.


๐Ÿ— Architectural Snapshot

  • Framework: Flask 3.x
  • WSGI Host: Passenger
  • Content Layer: Markdown โ†’ HTML rendering
  • API Layer: Flask-RESTful
  • Routing: Blueprints (routes/)
  • No database (filesystem-driven content)
  • No containerization
  • Internet-facing project separate from DEV mental model

๐ŸŸข Important Clarification (Long-Term Memory Anchor)

Virtualenv = interpreter isolation
Passenger = process manager
Flask = application framework
Project folder = application code
Spells folder = content layer

These are separate layers.


This version is:

  • Accurate
  • Cleaner
  • Layer-aware
  • Future-proof
  • Portable into documentation

Now one last question for Entry #1 refinement:

Do you want to explicitly document:

Why Passenger instead of Gunicorn?

Or is that outside the scope of this chapter?

ย 


๐Ÿโœจ Our Python Kingdom Is Born!

๐Ÿงฑ A custom Python 3.8 environment
๐Ÿ”ฅ A working Flask + Jinja2 app
๐Ÿงž Passenger obeying your WSGI commands
๐Ÿ“‚ Your own templates, routes, and static files
๐Ÿ› ๏ธ A restart script you actually understand
๐Ÿšซ No containers, no GUI fluff, no weird magic

 _____________________________
< Welcome to Python Sovereignty >
 -----------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
My Server. My Toys. My Way.

๐Ÿชง Now take this oath:

"I shall debug my own logs.
I shall tame passenger_wsgi.py.
I shall not fear the venv.
And I shall never again run pip install as root."


This is a milestone. Your server is no longer just hosting apps โ€”
it's serving your will.

The Forge is operational.

๐Ÿง  How Python resolves imports in a venv

When you do:

import flaskย 

Python looks in this order (simplified):

  1. sys.path[0] โ†’ current scriptโ€™s directory

  2. venvโ€™s site-packages/

    • e.g. ~/virtualenv/comics.eckford.ca/3.8/lib/python3.8/site-packages/

  3. Standard library paths

    • e.g. /usr/lib/python3.8/

  4. System site-packages/

    • ONLY if your venv was created with --system-site-packages

    • otherwise, these are ignored


โœ… Minimal steps from a fresh terminal:

ssh your_user@your_server  # if you're not already in
cd ~/comics.eckford.ca
source ~/virtualenv/comics.eckford.ca/3.8/bin/activate

After that, you're in the virtualenv and ready to run:

python app.py

Or launch passenger_wsgi from tmp and it starts app.py


๐Ÿง  Notes:

  • You donโ€™t have to cd into the project directory to activate the venv, but it's usually what you want โ€” especially if app.py is there.
  • The venv doesn't care where you run source from โ€” it just adjusts your environment.
  • You can have many terminals using the same venv at once. Itโ€™s just a directory.


((comics.eckford.ca:3.8)) [eckfordc@s12751 comics.eckford.ca]$ python 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:5000
Press CTRL+C to quit

http://127.0.0.1:5000

# DO NOT REMOVE. CLOUDLINUX PASSENGER CONFIGURATION BEGIN
PassengerAppRoot "/home/eckfordc/comics.eckford.ca"
PassengerBaseURI "/"
PassengerPython "/home/eckfordc/virtualenv/comics.eckford.ca/3.8/bin/python"
# DO NOT REMOVE. CLOUDLINUX PASSENGER CONFIGURATION END

PassengerFriendlyErrorPages off
PassengerLogLevel 1

# DO NOT REMOVE OR MODIFY. CLOUDLINUX ENV VARS CONFIGURATION BEGIN
<IfModule Litespeed>
</IfModule>
# DO NOT REMOVE OR MODIFY. CLOUDLINUX ENV VARS CONFIGURATION END

ย 

๐Ÿงญ Now What?

If you want to get this reachable from the browser (comics.eckford.ca), youโ€™ll need to:

  1. Reconnect passenger_wsgi.py

  2. Let Passenger auto-start the app again

  3. python is now Python 3.9.25 (note: check passenger_wsgi configย 
    PassengerPython "/home/eckfordc/virtualenv/comics.eckford.ca/3.8/bin/python"

But now that python app.py works โ€” we're almost there..
We can confidently tweak, test, or rebuild passenger_wsgi.py knowing the core is healthy.


/home/eckfordc/virtualenv/comics.eckford.ca/3.8/bin/python --version
Python 3.8.20
ย 

Restart Passenger

Restart Passenger by creating or touching a file:

touch tmp/restart.txtย 

Passenger watches for this and will reload your app.

((comics.eckford.ca:3.8)) [eckfordc@s12751 comics.eckford.ca]$ sh restart.sh
Passenger restart triggered via /home/eckfordc/comics.eckford.ca/tmp/restart.txt

ย 

ย 

((comics.eckford.ca:3.8)) [eckfordc@s12751 comics.eckford.ca]$ sh restart.shย 
Passenger restart triggered via /home/eckfordc/comics.eckford.ca/tmp/restart.txt

Hello from Flask!

ย 

ย 

Or you can go in to tmp to run it but seriously?

#!/bin/bash
# restart.sh ๏ฟฝ~@~T sane wrapper for Passenger app reload
APP_DIR="$(cd "$(dirname "$0")" && pwd)"
TMP_DIR="$APP_DIR/tmp"
RESTART_FILE="$TMP_DIR/restart.txt"
# Step 1: ensure tmp exists
mkdir -p "$TMP_DIR"
# Step 2: trigger Passenger restart
touch "$RESTART_FILE"
# Step 3: show a friendly message
echo "Passenger restart triggered via $RESTART_FILE"

Flask:

"Hi, I'm ready on 127.0.0.1:5000. Use me wisely."

Passenger:

"Greetings traveler. To proceed, touch the sacred scroll in /tmp. But firstโ€ฆ you must guess its name."

python -m site

((comics.eckford.ca:3.8)) [eckfordc@s12751 comics.eckford.ca]$ chmod +x restart.sh
((comics.eckford.ca:3.8)) [eckfordc@s12751 comics.eckford.ca]$ sh restart.sh 
Passenger restart triggered via /home/eckfordc/comics.eckford.ca/tmp/restart.txt
((comics.eckford.ca:3.8)) [eckfordc@s12751 comics.eckford.ca]$ vi restart.sh
((comics.eckford.ca:3.8)) [eckfordc@s12751 comics.eckford.ca]$ python -m site
sys.path = [
    '/home/eckfordc/comics.eckford.ca',
    '/opt/alt/python38/lib64/python38.zip',
    '/opt/alt/python38/lib64/python3.8',
    '/opt/alt/python38/lib64/python3.8/lib-dynload',
    '/home/eckfordc/virtualenv/comics.eckford.ca/3.8/lib64/python3.8/site-packages',
    '/home/eckfordc/virtualenv/comics.eckford.ca/3.8/lib/python3.8/site-packages',
]
USER_BASE: '/home/eckfordc/.local' (exists)
USER_SITE: '/home/eckfordc/.local/lib/python3.8/site-packages' (doesn't exist)
ENABLE_USER_SITE: False

ย 

Installation

To install Flask inside a Python virtual environment (venv) for Python 3.8.20, follow these steps:


โœ… Step-by-step: Install Flask in a venv

1. Create the virtual environment

python3 -m venv venv

This creates a folder called venv in your current directory.


2. Activate the virtual environment

On Linux/macOS:

source venv/bin/activate

Your shell prompt should now be prefixed with (venv).


3. Upgrade pip (recommended)

pip install --upgrade pip

4. Install Flask

pip install Flask
Installing collected packages: zipp, MarkupSafe, itsdangerous, click, blinker, Werkzeug, Jinja2, importlib-metadata, Flask
Successfully installed Flask-3.0.3 Jinja2-3.1.6 MarkupSafe-2.1.5 Werkzeug-3.0.6 blinker-1.8.2 click-8.1.8 importlib-metadata-8.5.0 itsdangerous-2.2.0 zipp-3.20.2

5. Verify Installation

python -c "import flask; print(flask.__version__)"
((comics.eckford.ca:3.8)) [eckfordc@s12751 comics.eckford.ca]$ python -c "import flask; print(flask.__version__)"
<string>:1: DeprecationWarning: The '__version__' attribute is deprecated and will be removed in Flask 3.1. Use feature detection or 'importlib.metadata.version("flask")' instead.
3.0.3

6. (Optional) Create a minimal test app

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello from Flask!"

if __name__ == "__main__":
    app.run()
~                                                                                                    
~                     

the old version called passenger. so with that silenced the errors should be still as well

from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    return "๏ฟฝ~\~E Flask is alive!"

# Required for Passenger
# application = app

~                                                                                                                  
~                

ย 

Run it

python app.py

source /home/eckfordc/virtualenv/comics.mysite.ca/3.8/bin/activate && cd /home/eckfordc/comics.mysite.ca
((comics.eckford.ca:3.8)) [eckfordc@s12751 comics.mysite.ca]$ ps aux | grep python
eckfordc 1838054  0.0  0.0   3340  1536 pts/0    S+   02:44   0:00 grep --color=auto python

ย 

Python 3.8.20 (default, Sep  7 2024, 00:00:00)
[GCC 11.4.1 20231218 (Red Hat 11.4.1-3)] on linux
Type "help", "copyright", "credits" or "license" for more information.
# extension module 'readline' loaded from '/opt/alt/python38/lib64/python3.8/lib-dynload/readline.cpython-38-x86_64-linux-gnu.so'
# extension module 'readline' executed from '/opt/alt/python38/lib64/python3.8/lib-dynload/readline.cpython-38-x86_64-linux-gnu.so'
import 'readline' # <_frozen_importlib_external.ExtensionFileLoader object at 0x7ff70e9bb4f0>
import 'atexit' # <class '_frozen_importlib.BuiltinImporter'>
# /opt/alt/python38/lib64/python3.8/__pycache__/rlcompleter.cpython-38.pyc matches /opt/alt/python38/lib64/python3.8/rlcompleter.py
# code object from '/opt/alt/python38/lib64/python3.8/__pycache__/rlcompleter.cpython-38.pyc'
import 'rlcompleter' # <_frozen_importlib_external.SourceFileLoader object at 0x7ff70e95a790>

ย 

Dig a little deeper... into the VENV container I presume. How deep is this?ย 

((comics.eckford.ca:3.8)) [eckfordc@s12751 comics.eckford.ca]$ python -c "import flask; print(flask.__version__)"
<string>:1: DeprecationWarning: The '__version__' attribute is deprecated and will be removed in Flask 3.1. Use feature detection or 'importlib.metadata.version("flask")' instead.
3.0.3
((comics.eckford.ca:3.8)) [eckfordc@s12751 comics.eckford.ca]$ ll
total 32
drwxr-xr-x 2 eckfordc eckfordc 4096 Jan 10 03:33 __pycache__
-rw-r--r-- 1 eckfordc eckfordc  112 Jan 24 02:36 app.py
drwxr-xr-x 2 eckfordc eckfordc 4096 Jan  2 20:26 cgi-bin
-rw-r--r-- 1 eckfordc eckfordc    0 Jan  2 04:16 imported_pip_requirements.txt
-rw-r--r-- 1 eckfordc eckfordc  197 Jan 10 03:48 passenger_wsgi.py
drwxr-xr-x 2 eckfordc eckfordc 4096 Jan  1 04:06 public
drwxr-xr-x 2 eckfordc eckfordc 4096 Jan 10 03:00 static
drwxr-xr-x 2 eckfordc eckfordc 4096 Jan 10 02:57 templates
drwxr-xr-x 2 eckfordc eckfordc 4096 Jan  1 04:06 tmp
((comics.eckford.ca:3.8)) [eckfordc@s12751 comics.eckford.ca]$ python app.py
((comics.eckford.ca:3.8)) [eckfordc@s12751 comics.eckford.ca]$ python
Python 3.8.20 (default, Sep  7 2024, 00:00:00) 
[GCC 11.4.1 20231218 (Red Hat 11.4.1-3)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import flask; print(flask.__version__)
<stdin>:1: DeprecationWarning: The '__version__' attribute is deprecated and will be removed in Flask 3.1. Use feature detection or 'importlib.metadata.version("flask")' instead.
3.0.3
>>> 

ย 

((comics.eckford.ca:3.8)) [eckfordc@s12751 comics.eckford.ca]$ python app.py

http://127.0.0.1:5000

((comics.eckford.ca:3.8)) [eckfordc@s12751 comics.eckford.ca]$ python 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:5000
Press CTRL+C to quit

Would you like a requirements.txt for deployment or offline install too?

ย 

ย