Arch Linux – Deploying a Pure Python Web App

Deploying a web app written in Python is not always easy. Here are a couple of easy steps to get it done on Arch Linux. These steps are not a perfect tutorial if you want to go for high traffic sites. It is more about automating the start-up of the web app when you boot up the system.

The part describing how to set up a virtual environment for Python packages is only useful if your web app depends on external Python modules. You could also install them system-wide but this way allows you to keep different versions for different software.

Here are the steps:

# Update the system
pacman -Syyu

# Install git
pacman -S git python3

# Create a new group (if it doesn't exist) and a user account
groupadd whatsmyip
useradd -m -g whatsmyip -s /bin/bash whatsmyip

# Log in as that user
su whatsmyip

cd ~/
git clone git://github.com/pklaus/WhatsMyIP.git

# Set up a virtual python environment
# see https://blog.philippklaus.de/2014/08/setting-up-python-venvs-on-arch-linux-arm/
python3 -m venv ~/.pyvenv/whatsmyip-3.4
source ~/.pyvenv/whatsmyip-3.4/bin/activate

# Test run the python app:
cd ~/WhatsMyIP/
./whatsmyip.py

Then create a systemd startup script to run your web server:

A simpler variant involves using crontab:

su
pacman -S cronie
systemctl enable cronie
systemctl start cronie

su whatsmyip

cat << "EOF" > /home/whatsmyip/start_server.sh
#!/bin/bash

sleep 20
source ~/.pyvenv/whatsmyip-3.4/bin/activate
/home/whatsmyip/WhatsMyIP/whatsmyip.py
EOF

chmod +x /home/whatsmyip/start_server.sh

crontab -e
# -- there add:
# m h dom mon dow user  command
@reboot /home/whatsmyip/start_server.sh

If you need the script to run on a port like 80, create the file ~whatsmyip/app/run.py like start-and-drop-privs.py and start that as root. It will start the server listening on port 80 and then drop the root privileges:

cd ~whatsmyip/app/
chmod +x run.py
./run.py

Instead, you can also run it as a local user and set up Firewall rules to forward incoming traffic directed at port 8080 to port 80:

iptables  -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
ip6tables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080

You can read more here on how to store these firewall rules permanently. The important steps are:

cat << "EOF" > /etc/iptables/iptables.rules
# Generated by iptables-save v1.4.21 on Thu Aug 3 03:16:44 2014
*nat
:PREROUTING ACCEPT [93:10977]
:INPUT ACCEPT [42:3823]
:OUTPUT ACCEPT [16:1155]
:POSTROUTING ACCEPT [16:1155]
-A PREROUTING -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 8080
COMMIT
# Completed on Thu Aug 3 03:16:44 2014
EOF

cat << "EOF" > /etc/iptables/ip6tables.rules
# Generated by ip6tables-save v1.4.21 on Thu Aug 3 03:17:12 2014
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [2:160]
:POSTROUTING ACCEPT [2:160]
-A PREROUTING -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 8080
COMMIT
# Completed on Thu Aug 3 03:17:12 2014
EOF

systemctl enable iptables
systemctl enable ip6tables

The nat target is available on Linux kernel 3.8+ and ip6tables v1.4.18+ according to IPV6 nat pre-routing with iptables.

Notes

In a previous version of this blog post, I used python's virtualenvwrapper (pacman -S python-virtualenvwrapper) instead of pyvenv.

Resources

Comments