Alexandre Petit

Deploy Python Web Apps on your own server

Temps de lecture : 4 min.

Why ?

Why would you do that ?

Here are 5 reasons that motivated me :

Another reason that pushed me is mimesis. Pieter Levels deploy his index.php files on his own server. Derek Sivers run all his business on it's own sever. Hackers do it. It looks exciting.

So, here is the plan :

Setup the instance

This article sit on the shoulders of https://sive.rs/ti.

Follow it to setup your own server. Derek's article will lead you to a simple, secure, and powerful setup.

His guide is frequently updated. But still, things can go wrong. If it does, start over. You'll be amazed how fast you can go. If it still doesn't work, yo are lucky. It's an opportunity to learn and help. Read the ti.sh source instructions. Dig the man pages. It's most certainly not a big thing and you will learn a lot.

By the end of the article, you are equipped with your own server. Setup of e-mail, calendar and contacts is out of scope in our case. If you seize the opportunity, great :).

Create a simple app

1. Connect to your server with ssh

2. Run whoami and note your username (mine is just a)

3. Install python :

doas pkg_add python

4. Create a folder for your app

cd /var/www/
doas su
mkdir myapp
chown -R <username>:<username> myapp  # for me it's `chown -R a:a myapp`
exit

5. Download code of your app

cd myapp
curl -O https://petitapetit.io/pub/app.py
# or git clone <https://github.com/....git> .

6. Create a virtual environment and install the dependencies

python3 -m venv .venv
. .venv/bin/activate
pip install --upgrade pip
pip install flask
pip install gunicorn

7. Test that it works locally

First, with flask :

# Serve the web app
python -m flask --app app run -h 127.0.0.1 -p 8000
# In another window, browse it. You should the html content of the page.
curl 127.0.0.1:8000

Then, with gunicorn :

# Serve the web app
gunicorn app:app --workers 1 --bind 127.0.0.1:8000 --daemon
# Again, browse it
curl 127.0.0.1:8000

# Stop the gunicorn process 
ps aux | grep gunicorn
kill -9 <GUNICORN_PID>

8. Configure a daemon

Create a file with doas vim /etc/rc.d/myappd and paste this content inside :

#!/bin/ksh

venv="/var/www/myapp/.venv"
daemon="${venv}/bin/gunicorn" 
daemon_flags="app:app --daemon --workers 2 --bind 127.0.0.1:8000 --chdir /var/www/myapp"

. /etc/rc.d/rc.subr

rc_user="www"
rc_env="PATH=${venv}/bin:/usr/bin:/bin"

rc_start() {
    echo "Start the myapp gunicorn daemon"
    ${rcexec} ${daemon} ${daemon_flags}
}

rc_check() {
    pgrep -f "${venv}/bin/python3 ${venv}/bin/gunicorn app:app" >/dev/null
}

rc_stop() {
    echo "Kill the myapp gunicorn workers"
    pkill -f "${venv}/bin/python3 ${venv}/bin/gunicorn app:app" 
}

rc_cmd $1

9. Enable the daemon so that it will start automatically if you reboot your server.

rcctl enable myappd

10. Start your daemon

rcctl start myappd

In case of troubles, read man rcctl or try /etc/rc.d/myappd -d start to start the deamon manually and see the logs.

Add your subdomain to your certificate

1. Add your subdomain to the acme-client configuration.

To do so, edit /etc/acme-client.conf and add your subdomain to the list of alternative names of your domain.

Here is how the modification look on my side :

updated-acme-client-conf

2. Renew your certificate

doas acme-client -v <your-domain> # for me it's `doas acme-client -v petitapetit.io`

Route the trafic to your web app

1. Update your relayed configuration to route requests made to your subdomain toward your web app.

Open the configuration with doas vim /etc/relayd.conf. Then add the following lines :

updated-relayd-conf.png

2. Apply the changes and load the new certificate

doas rcctl reload relayd

3. Open a browser and try to browse your subdomain

For the purpose of this tutorial, it would be https://myapp.petitapetit.io on my server. You can visite https://leequotes.petitapetit.io to see a web application that I'm actively serving.

Conclusion

You can now deploy an infinite number of python web apps, on your own server.

How does it feel ?

If something goes wrong, please give me a description of what goes wrong at what step. If you make it work, please tell me. I would be happy to know it helped you. Finally, if you see something that can be improved, please tell me too.

Publié le

Une remarque ? Dis-moi 💙