Monday, December 8, 2025

Create Indestructable SBC's with overlayfs--Part II--uv, flask, and systemd

Last time I went through using overlayfs on a Linux SBC to survive unwelcomed power hits. 

Today's post is Part II of a two-parter; you may want to read or skim the last post.

I wanted to finish the indestructable SBC setup by adding a python virtual environment using uv, getting a basic webserver "hello world" going using Python flask, and getting the flask service to autostart at boot using systemd.

If you are a Linux/Python admin you probably already know how to do all of this; you should probably skip this post and check out these guys instead. 

As usual, I am writing the steps down in this post because I forget everything. These steps worked for me 12-8-25, hopefully if you read this a few years hence some of will still work (and AI will do the whole thing for you?)

Only 512MB but text only OS still gave me space to mess around in an overlayfs configuration

BUT FIRST--A WORD FROM THIS BLOG'S SPONSOR:


               
 

After getting your SBC set, you will probably want to design a PCB for your SBC then get them fabricated.

For this, please check out PCBWAY. 

This month (December 2025) PCBWAY is featuring some awesome holiday specials, details here.

PCBWAY has a special where cool PC colors like purple are charged at their super low price: ten 99 x99 mm double sided PCB's for $5USB + tax, tariff, shipping.  Super affordable!

PCBWAY also has a service to fabricate PCBs and other materials using any and all visible colors. Full color PCB's! Details here

In addition to top shelf PCB fabrication they also do fantastic work with assembly3D printinginjection molding, and much more. 

Their staff is extremely helpful and PCBWAY always turns work around quickly. 

As always--you can help this blog by checking out the PCBWAY site. Thanks.

MAKING CHANGES TO THE READ ONLY SBC

From last post: we set the Raspberry Lite OS to be read-only to survive power hits using overlayfs, but today we are making changes to the OS. 

How?

  • I temporarily made my RPi Zero's OS writable by:
  •  removing its SD card
  • mounting it via a USB adapter on my PC  editing /boot/firmware/config.txt, getting rid of overlayroot=tempfs at the end of the statement, 
  • then saved cmdline.txt. 

Then I popped the SD back into the RPI and started it up--it was now a normal read-write device.

When I was ready to go back to indestructable mode, I added overlayroot=tempfs back to the cmdline.txt using vi, saved, and reboot. 

WHAT IS UV?

I've been a python programmer for years and all the tools needed to set up/maintain python3 and Python projects-- pip, pip-tools, pipx, poetry, pyenv, twine, virtualenv, and so on, drove me nuts. 

I had a preflight checklist for creating new virtual environments and I'd still make stupid mistakes.

Imagine my excitement when a tech from my geeky tech group told me about uv, a single tool that kicks the other Python admin tools' butts. 

Count me in!

I read about uv here; good getting started video is here.  

For Raspberry Pi OS here are the commands I used to create a new project called pyflask using uv:

#hint: to make my SSH terminal not look crap, make sure to use UTF-8 encoding (I use secureCRT9.6 #and properties > appearace > encoding default is "automatic" which isn't working)

#install uv 

apt update

apt install curl #needed for next command--may already be installed on your SBC

#install uv using less HD space vs using pip install.

curl -LsSf https://astral.sh/uv/install.sh | sh

#SET PATH needed for terminal app to find uv when you try to run it

#put into path--for rpi I could skip this step and just restart bash; not sure w othr distros

echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc

#for some distros .local above is replaced by .cargo

#restart bash  

bash  

#is uv working?  This should show a list of basic uv commands.

 uv

CREATE A NEW PYTHON PROJECT AND VIRTUAL ENV USING UV

#cd to ~ you should do the next command from your home dir.

#my home dir is /home/charlie, yours probably isn't.

uv init pyflask #pyflask is the name of the project

#cd to the project folder you just created.

cd ~/pyflask

uv run main.py  

#creates venv and show you successful hello world.cd p

#ACTIVATE THE NEW PROJECT

#you still must be in the project dir.

#must use source here or term will open

#a new term, make the change, and pop you back to old one....

#not what we want...

source .venv/bin/activate 


#INSTALL PYTHON PACKAGES

#in project root folder....

#uv add [whatever]  # use this instead of pip, faster!

#I love how fast uv is at adding modules!

#much MUCH faster vs. pip install.

#let's add flask!

uv add flask

#ASIDE--with UV, WHERE DO YOUR FILES .py files GO?

#see what is where in uv--useful cmd!

uv tree

#in project root.  See below. note-- never edit anything inside the .venv dir and its subdirs.

#/home/pi/my_weather_bot/       <-- YOUR PROJECT ROOT (You work here)

#├── main.py                    <-- Your code goes here

#├── utils.py                   <-- Your code goes here

#├── data/                      <-- Your data folders go here

#│   └── weather_log.csv

#└── .venv/                     <-- THE VIRTUAL ENV (Do not open/edit)

 #   ├── bin/

  #  ├── lib/

   # └── pyvenv.cfg


After all of this I had a working venv for pyflask. Good start!



GETTING FLASK GOING....

Flask is a simple webserver for Python--more here.  By using flask, I created a webserver that generated "hello world" when viewed from my browser--using only a few lines of code--super easy!

#I created the file below using vi in /home/charlie/pyflask/app.py

####################SNIP ###############

#we used uv to install the flash module in our venv already.

from flask import Flask 

app = Flask(__name__)

@app.route('/')

def hello():

    return 'Hello, World!'

if __name__ == '__main__':

    app.run(host='0.0.0.0', port=5000, debug=True)

#START PYTHON APP using UV

#use this to test your flask setup.

uv run python3 app.py  

#if you just use python3 app.py the path gets 

#wacked and it won't work


###what your term will show (if flask is ready to process browserGETS and #PUTS)

 #* Serving Flask app 'app'

 #* Debug mode: on

#WARNING: This is a development server. Do not use it in a production #deployment. Use a production WSGI server instead.

 #* Running on all addresses (0.0.0.0)

 #* Running on http://127.0.0.1:5000

 #* Running on http://192.168.5.200:5000

#Press CTRL+C to quit

 #* Restarting with stat

 #* Debugger is active!

 #* Debugger PIN: 430-777-572

####################SNIP ###############



ACCESSING THE FLASK WEBPAGE FROM A BROWSER

Once I had everything above working it was time to test from a browser.

#if I open browser or curl using PC on

#the same wifi subnet as RPi Zero using port 5000 

#I see hello world.  

Your IP will be different, but I hope you get the idea.

http://192.168.5.200:5000/

GETTING FLASK TO AUTO-START

Our indestructable SBC's test app (in this case app.py) had to start on power-up every time.  

There were lots of ways I could have done this, but to me the "Debian way" was to use systemd--good video series about how systemd works starts here; let's get right to it.

#we need to create a .service file for our python app.

#in Raspberry pi and maybe other debian likes create this in this dir.

#/etc/systemd/system/

#remember that systemd only works with

#the full path to a file or executable

#the command to create the .service file using vi on a raspberry pi is this.

sudo vi /etc/systemd/system/flaskapp.service


###########SNIP!#############################

 [Unit]

Description=My Flask App

# Wait for the network to be ready before starting (crucial for Flask)

After=network.target


[Service]

# The user the app should run as 

User=charlie

# The group  

Group=charlie


# The folder where your app.py is located

WorkingDirectory=/home/charlie/pyflask


# The command to start the app. 

# format: /path/to/python /path/to/app.py

#we are using uv, so, we use the uv created venv virtual directory.

ExecStart=/home/charlie/pyflask/.venv/bin/python3 /home/charlie/pyflask/app.py


# Automatically restart the app if it crashes

Restart=always


# Send Python output to the system log immediately

Environment=PYTHONUNBUFFERED=1


[Install]

WantedBy=multi-user.target


###########SNIP!#############################

FINISHING UP OUR BUILD

#let's load it!  first, make the system reread all systemd config files...

sudo systemctl daemon-reload

#enable at boot

sudo systemctl enable flaskapp.service

#start it

sudo systemctl start flaskapp.service

#see its status

sudo systemctl status flaskapp.service

#stop the service (no changes needed to the service file for this)

sudo systemctl stop flaskapp.service

#restart the service

sudo systemctl restart flaskapp.service

OUTTRO

I have configured the steps below on a Raspberry Pi Zero W, and it works great--I am ready to start building audio projects that are Linux and Python based, that power up and down like any other DiWHY project, ones that can interact with a web browser on the same subnet. Cool.

Now that I have a framework for a SBC based DiWHY project, what should I build?  

No idea. 

I have read about connecting CODECS and DACS to Raspberry Pi's, I will probably start there and design my own experimenters' boards, but for now, it's time to be inhaling no fumes. 

See ya next time!

 

No comments:

Post a Comment

Create Indestructable SBC's with overlayfs--Part II--uv, flask, and systemd

Last time I went through using overlayfs on a Linux SBC to survive unwelcomed power hits.  Today's post is Part II of a two-parter; you...