Ashari Abidin's Developer Docs

Flash Gunicorn Systemd

Production-grade deployment

Run Flask Permanently on Ubuntu

Gunicorn + systemd ยท Auto-restart ยท Survives reboots & disconnections
Standard Architecture (Production)
๐ŸŒ Internet โš™๏ธ Nginx (80/443) ๐Ÿ Gunicorn ๐Ÿ“ฆ Flask App ๐Ÿฌ MySQL
Minimal: Internet โ†’ Gunicorn โ†’ Flask โ†’ MySQL
Explanation: Nginx acts as a reverse proxy (handles static files, SSL, load balancing). Gunicorn is the WSGI server that runs your Flask code. systemd ensures the process stays alive even after reboots or crashes.
1Connect to your Ubuntu server
ssh ubuntu@SERVER_IP
Explanation: The ssh command securely connects to your remote server. Replace SERVER_IP with your server's public IP address. Example: ssh ubuntu@115.178.xxx.xxx. You will be prompted for a password or key.
2Navigate to your application directory
cd ~/upload/regapicta
Explanation: cd changes the current directory to your Flask project folder. ~/upload/regapicta is an example path; adjust it to where your app lives. Run ls -la to verify that app.py, venv/, and other files exist.
3Activate the Python virtual environment
source venv/bin/activate
Explanation: The virtual environment isolates project dependencies. source runs the activate script, changing your prompt to (venv) user@server:.... All subsequent pip install commands will affect only this project.
4Install Gunicorn
pip install gunicorn
gunicorn --version
Explanation: Gunicorn is a production-grade WSGI HTTP server for Flask. pip install gunicorn installs it inside the active virtual environment. gunicorn --version confirms successful installation.
5Test the application with Gunicorn

Assume your main Flask file is app.py containing app = Flask(__name__)

gunicorn -w 4 -b 0.0.0.0:5000 app:app
ParameterMeaning
-w 4Number of worker processes (4)
-b 0.0.0.0:5000Bind to all network interfaces on port 5000
app:appImport app object from app.py file
Explanation: This starts Gunicorn with 4 worker processes to handle concurrent requests. 0.0.0.0 makes the server accessible from outside, port 5000. app:app means "from file app.py, import the variable app". Test by opening http://SERVER_IP:5000 in a browser. Press Ctrl+C to stop.
6Browser access test
http://SERVER_IP:5000
Explanation: If your Flask page loads, Gunicorn is correctly configured. Stop it with Ctrl+C before proceeding to systemd setup.
7Create a systemd service
sudo nano /etc/systemd/system/regapicta.service

Insert the following configuration:

[Unit]
Description=Regapicta Flask Application
After=network.target

[Service]
User=ubuntu
Group=ubuntu
WorkingDirectory=/home/ubuntu/upload/regapicta
ExecStart=/home/ubuntu/upload/regapicta/venv/bin/gunicorn -w 4 -b 0.0.0.0:5000 app:app
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
Explanation of each section:
โ€ข [Unit]: Metadata; After=network.target ensures the service starts after the network is ready.
โ€ข [Service]: Defines the user/group, working directory, and the exact command to run Gunicorn (using the full path to the venv binary).
โ€ข Restart=always: systemd will automatically restart the app if it crashes.
โ€ข RestartSec=5: Wait 5 seconds before restarting.
โ€ข [Install]: WantedBy=multi-user.target means the service will start automatically at boot.
Save with CTRL+O, ENTER, then CTRL+X.
8Reload systemd
sudo systemctl daemon-reload
Explanation: This command tells systemd to reload all unit files. It is required after creating or modifying a .service file so that systemd recognizes the new configuration.
9Enable automatic startup on boot
sudo systemctl enable regapicta
Explanation: enable creates a symbolic link so the service starts automatically every time the server boots. Without this, you would need to manually start it after each reboot.
10Start the service
sudo systemctl start regapicta
Explanation: Starts the service immediately. Use start whenever you want to run the service after it has been stopped.
11Check service status and logs
sudo systemctl status regapicta
sudo journalctl -u regapicta -f
sudo journalctl -u regapicta -n 100
Explanation: status shows whether the service is active (running). journalctl -u name -f follows live logs (like tail -f). -n 100 displays the last 100 log lines. Logs are essential for debugging application errors.
12Daily operations
sudo systemctl stop regapicta # stop the service
sudo systemctl start regapicta # start the service
sudo systemctl restart regapicta # restart (after code changes)
sudo systemctl status regapicta # check health
Explanation: stop halts the application, start launches it, restart is a convenient stop+start sequence used after updating Flask files. No need to reboot the server.
13Deploy code changes
cd ~/upload/regapicta
git pull # or edit files manually
sudo systemctl restart regapicta
Explanation: After modifying your Flask application (routes, models, templates), you must restart Gunicorn to load the new code. systemd will stop the old workers and start fresh ones with the updated version.
14Verify it works without SSH

Disconnect: exit or simply close your laptop. Then try accessing http://SERVER_IP:5000 from a browser.

Explanation: Because the service is managed by systemd, the application continues running as a background daemon even after the SSH session ends. systemd keeps Gunicorn alive independently.
15Verify automatic startup after reboot
sudo reboot
Explanation: After the server reboots, reconnect via SSH and run sudo systemctl status regapicta. You will see active (running) without any manual intervention. This proves that enable successfully configured the service to start at boot.
โš™๏ธStartup process: Before vs After systemd

BEFORE systemd

SSH โ†’ activate venv โ†’ python app.py โ†’ site works. If SSH disconnects โ†’ app stops โŒ

AFTER systemd

Ubuntu boots โ†’ systemd reads regapicta.service โ†’ executes Gunicorn โ†’ Flask becomes available automatically โœจ

Technical explanation: systemd is the modern init system on Ubuntu. At boot, it parses .service files and launches the process defined in ExecStart. With Restart=always, if the process dies (crash), systemd restarts it within 5 seconds.
๐Ÿš€Recommended production setup

Add Nginx as a reverse proxy + Let's Encrypt SSL. Final architecture:

๐ŸŒ Internet โ†’ ๐Ÿ›ก๏ธ Nginx (80/443) โ†’ ๐Ÿ Gunicorn (5000) โ†’ ๐Ÿ“„ Flask App โ†’ ๐Ÿฌ MySQL
Explanation: Nginx receives client requests (ports 80/443) and forwards them to Gunicorn on localhost:5000. Nginx efficiently serves static files and manages SSL certificates (HTTPS), making it the standard production reverse proxy for Flask.
๐Ÿ“‹Useful systemd commands
sudo systemctl daemon-reload # after editing .service file
sudo systemctl restart regapicta # restart the application
sudo journalctl -u regapicta -f # follow live logs
sudo systemctl is-enabled regapicta # check if service starts at boot
Explanation: is-enabled returns enabled if the service is set to auto-start. restart is commonly used after git pull or file updates to apply changes immediately.

Back