How I set up a headless Ubuntu VPS for hosting Python apps with Nginix, Gunicorn, Flask and Celery:
sudo nginx -t make sure Nginx configuration is valid
sudo ufw status see which ports are open in the firewall
sudo systemctl status [SERVICE] check the status of a service
sudo systemctl daemon-reload sudo systemctl restart [SERVICE] restart a serviceMore systemctl commands:
sudo systemctl start sudo systemctl stop
sudo systemctl enable sudo systemctl disable
cd /var/www go to the web root
mkdir celeryflask cd celeryflask create and go into a new project directorypython3 -m venv celeryflask-env create a Python virtual environment in a new directory 'celeryflask-env'
source celeryflask-env/bin/activate activate the virtual environment
pip install wheel pip install flask gunicorn install Wheel, Flask & Gunicorn to the virtual environment
vim app.py create a file for the Flask application
sudo ufw status sudo ufw allow 5005 make sure your chosen port is open, open the port if necessary
from flask import Flask app = Flask(__name__) @app.route("/") def hello (): return "Hello There!" if __name__ == "__main__": app.run(host='0.0.0.0', port=5005, debug=True)
python app.py run the Flask app with the built-in development server
-test the website-
ctrl-c quit the dev server
deactivate deactivate the virtual environment
Note: when running Redis but not as a service, you have to specify the config file to load.
Redis comes with its own systemctl service file.
Redis file locations:
/usr/bin/redis-server Redis binary
/etc/redis/redis.conf Redis config file
/etc/init.d/redis-service Redis service file
sudo systemctl start redis-server.service start Redis
sudo systemctl enable redis-server.service set Redis to run at boot
redis-cli ping returns PONG if Redis is up & running
source celeryflask-env/bin/activate activate the Python virtual environment
pip install redis pip install celery install redis-py (the Redis Python client package) and Celery to the virtual environment
vim tasks.py create a file for the Celery application
from celery import Celery app = Celery('tasks', backend='redis://localhost', broker='redis://localhost') @app.task def add(x, y): return x + y
tmux new -s celery-sesh start a new tmux session named celery-sesh
source celeryflask-env/bin/activate activate the virtual environment within the tmux session
celery -A tasks worker --loglevel INFO start a Celery worker on the Celery app 'tasks'
ctrl-b d detach from the tmux session
Other tmux commands:
tmux a -t celery-sesh reattach to tmux session celery-sesh
tmux ls list tmux sessions
Make sure celery is working:
python launch the python interpreter in interactive mode
>>> from tasks import add >>> result = add.delay(4, 4) >>> result.ready() >>> result.get(timeout=1) >>> exit()
tmux kill-server kill all tmux sessions
deactivate deactivate the virtual environment
source celeryflask-env/bin/activate activate the virtual environment
vim wsgi.py create the wsgi entry point
from app import app from app.py import the app variable if __name__ == "__main__": app.run()
gunicorn --bind 0.0.0.0:5005 wsgi:app run Gunicorn in the foreground
-test the website-
ctrl-c exit Gunicorn
deactivate deactivate virtual environment
cd /etc/systemd/system
sudo vim celeryflask-celery.service
[Unit] Description=Celery Service for celeryflask After=network.target [Service] User=casey Group=www-data WorkingDirectory=/var/www/celeryflask Environment="PATH=/var/www/celeryflask/celeryflask-env/bin" ExecStart=/var/www/celeryflask/celeryflask-env/bin/celery -A tasks worker -f celery.log [Install] WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl start celeryflask-celery
cd /var/www/celeryflask
source celeryflask-env/bin/activate
python
Make sure celery is working
>>> from tasks import add >>> result = add.delay(4, 4) >>> result.ready() >>> result.get(timeout=1) >>> exit()
deactivate
sudo systemctl enable celeryflask-celery
cd /etc/systemd/system
sudo vim celeryflask-gunicorn.service
[Unit] Description=Gunicorn Service for celeryflask After=network.target [Service] User=casey Group=www-data WorkingDirectory=/var/www/celeryflask Environment="PATH=/var/www/celeryflask/celeryflask-env/bin" ExecStart=/var/www/celeryflask/celeryflask-env/bin/gunicorn --reload --log-file gunicorn.log --bind 0.0.0.0:5005 wsgi:app [Install] WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl start celeryflask-gunicorn
-test the website-
sudo systemctl enable celeryflask-gunicorn
cd /etc/systemd/system
sudo vim celeryflask-gunicorn.service
Edit the service file to bind gunicorn to a socket instead of directly to the ip / port.
The sock file will be created the first time the service runs
ExecStart=/var/www/celeryflask/celeryflask-env/bin/gunicorn --reload --log-file gunicorn.log --bind unix:celerytut2.sock -m 007 wsgi:app
sudo systemctl daemon-reload
sudo systemctl restart celeryflask-gunicorn
cd /etc/nginx/sites-available
sudo vim celeryflask
server { listen 5005 default_server; server_name _; location / { include proxy_params; proxy_pass http://unix:/var/www/celeryflask/celeryflask.sock; # disable cache while in dev mode: proxy_no_cache 1; proxy_cache_bypass 1; } }
Link to celeryflask's sites-available in the sites-enabled directory:
sudo ln -s /etc/nginx/sites-available/celeryflask /etc/nginx/sites-enabled
sudo systemctl restart nginx restart Nginx
-test the website-