No Cache
Flask by default caches files on client devices. When in development, this is a goddamn headache. Especially when you're writing CSS. Unlike VSCode's live reload
. Syncing js and css changes require you to do a hard reload using ctrl + f5
. The code below counters this by specifying in the response header to not save anything.
# don't fucking save assets in cache
@app.after_request
def add_header(r):
r.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
r.headers["Pragma"] = "no-cache"
r.headers["Expires"] = "0"
r.headers["Cache-Control"] = "public, max-age=0"
return r
Deployment
- Setup database
- Create systemd service for gunicorn
- Create hook instance config json
- Configure GitHub repo's webhook settings
- Create a group for the hook service
- Create a systemd service for the hook instance
- Write the pull and reload script
Connecting a DB
First create a separate database and user for the web-app in the usual way. Then reference the db from the flask app. To get MariaDB working on SQLAlchemy you'll also need to install the pymysql
package1.
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+pymysql://<username>:<password>@localhost/<app-dbname>?charset=utf8mb4"
Gunicorn Service
To run a flask app in development you need a WSGI HTTP Server to serve and run it. This is where gunicorn
comes in.
Development
When in development you can run gunicorn
in the shell. The <app-name>
refers to the file name of the flask app and the app
refers to the name of the callable within it.
# run it on port 5000
gunicorn --bind localhost:5000 <app-name>:app
Production
When deploying, create a systemd service for the gunicorn
. See more info about creating systemd services here.
Find info about workers and optimizations here (opens in a new tab).
Here's an example systemd config for gunicorn.
[Unit]
Description=Gunicorn instance serving the app
After=network.target
[Service]
User=<reg_user>
Group=www-data
WorkingDirectory=/home/<reg_user>/project
ExecStart=/usr/local/bin/gunicorn --workers 3 --bind localhost:5000 <app-name>:app
[Install]
WantedBy=multi-user.target
Hook Instance
Create a webhook instance json file and connect it with Github to listen to push requests from the repo. More info here.
Then create a new group for the flask app and couple it with the already existing webhook
user created when setting up the webhook application. In it whitelist the systemctl
commands for dealing with the gunicorn service.
Write a systemd service file with the webhook
user and the new group for the hook instance. Find out how here.
Integration Script
Since systemd runs services in isolated environments. Without access to the user shell nor any environment variables. So, for private repos an ssh-agent needs to be started every time the script is run for Github authentication. More info here.
First clean the repo of any untracked changes. Then pull the latest commit. Find how here. Finally reload the site. Make sure to only use the systemctl
commands that were defined in the group the hook instance is running on.
systemctl restart <app-service>