Flask Gunicorn Service Failed to Start
If your Flask Gunicorn service fails to start in production, this guide shows you how to identify the startup failure, correct the systemd or Gunicorn configuration, and bring the app back online. Use it to fix failed units, bad paths, missing dependencies, environment issues, and socket binding errors step-by-step.
Quick Fix / Quick Setup
Run these commands first:
sudo systemctl status gunicorn --no-pager -l
sudo journalctl -u gunicorn -n 100 --no-pager
sudo systemctl cat gunicorn
# Validate the app manually as the service user
sudo -u www-data /path/to/venv/bin/gunicorn --bind 127.0.0.1:8000 wsgi:app
# After fixing paths/user/env, reload and restart
sudo systemctl daemon-reload
sudo systemctl restart gunicorn
sudo systemctl enable gunicorn
Most startup failures come from an incorrect ExecStart path, wrong working directory, missing virtualenv package, wrong WSGI module, or a service user that cannot access the app directory or socket.
What’s Happening
systemd starts Gunicorn using the unit file, configured user, working directory, environment, and ExecStart command. If any part of that chain is invalid, systemd marks the service as failed before Nginx can proxy requests to Flask.
Typical failures include import errors, missing Python packages, permission problems, bad bind targets, and invalid unit configuration.
Step-by-Step Guide
- Check the current failure state
Run:bashsudo systemctl status gunicorn --no-pager -l
Look for:code=exitedstatus=203/EXECPermission denied- Python traceback
- bind errors
- Read the Gunicorn service logs
Run:bashsudo journalctl -u gunicorn -n 200 --no-pager
Identify whether the failure happens:- before Gunicorn starts
- during Flask app import
- while binding the socket or port
- Inspect the active systemd unit
Run:bashsudo systemctl cat gunicorn
Verify these fields:User=Group=WorkingDirectory=Environment=EnvironmentFile=ExecStart=
- Confirm the Gunicorn binary exists in the expected virtualenv
Run:bashls -l /path/to/venv/bin/gunicorn source /path/to/venv/bin/activate pip show gunicorn flask
If packages are missing:bashpip install gunicorn flask - Verify the working directory and project files
Run:bashcd /path/to/app && ls -la
Confirm the Flask project root contains the expected entrypoint, such as:wsgi.pyapp.py
- Test the WSGI import manually
Run:bashcd /path/to/app source /path/to/venv/bin/activate python -c "from wsgi import app; print(app)"
If this fails, fix:- module names
- import paths
- app object name
- configuration loading during import
- Run Gunicorn manually as the service user
Use the same user configured in the unit file:bashsudo -u www-data bash -lc 'cd /path/to/app && /path/to/venv/bin/gunicorn --bind 127.0.0.1:8000 wsgi:app'
This is one of the fastest ways to expose:- path problems
- permission failures
- missing environment variables
- import errors
- Validate or replace the systemd service definition
Example working unit:ini[Unit] Description=Gunicorn for Flask app After=network.target [Service] User=www-data Group=www-data WorkingDirectory=/var/www/myapp Environment="PATH=/var/www/myapp/venv/bin" ExecStart=/var/www/myapp/venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 wsgi:app Restart=always [Install] WantedBy=multi-user.target
Save it as:bash/etc/systemd/system/gunicorn.service - Validate environment variables
If your app depends on secret keys, database URLs, or Flask settings, load them withEnvironment=orEnvironmentFile=.
Example:ini[Service] Environment="FLASK_ENV=production" Environment="SECRET_KEY=replace-me" EnvironmentFile=/etc/myapp/myapp.env
Then verify the file exists and is readable:bashls -l /etc/myapp/myapp.env - Check directory and file permissions
Run:bashnamei -l /var/www/myapp ls -ld /var/www/myapp ls -la /var/www/myapp
Confirm the service user can:- traverse parent directories
- read project files
- access the virtualenv
- create the socket file if using Unix sockets
- Validate port or Unix socket binding
For a TCP bind:bash/path/to/venv/bin/gunicorn --bind 127.0.0.1:8000 wsgi:app
Check whether the port is already in use:bashss -ltnp | grep 8000
For a Unix socket bind:bash/path/to/venv/bin/gunicorn --bind unix:/run/gunicorn/myapp.sock wsgi:app
Ensure the socket directory exists:bashsudo mkdir -p /run/gunicorn sudo chown www-data:www-data /run/gunicorn - Verify the systemd unit syntax
Run:bashsudo systemd-analyze verify /etc/systemd/system/gunicorn.service
Fix any invalid directives or formatting errors. - Reload systemd after changing the unit file
Run:bashsudo systemctl daemon-reload - Restart the service and verify it starts
Run:bashsudo systemctl restart gunicorn sudo systemctl status gunicorn --no-pager -l
Expected result:active (running)
- Confirm Gunicorn is listening
For TCP:bashss -ltnp | grep 8000 curl -I http://127.0.0.1:8000
For Unix sockets:bashss -lx | grep gunicorn - If Nginx is in front of Gunicorn, test the full stack
First validate Nginx:bashsudo nginx -t
Then test upstream connectivity and request flow. If Nginx still cannot reach Gunicorn, continue with Fix: Nginx Not Connecting to Gunicorn (Connection Refused).
For a complete baseline setup, see Deploy Flask with Nginx + Gunicorn (Step-by-Step Guide).
Common Causes
- Wrong
ExecStartpath → systemd cannot launch Gunicorn → pointExecStartto the correct virtualenv binary and reload systemd. - Incorrect
WorkingDirectory→ Gunicorn cannot import the Flask app module → set the project root correctly in the unit file. - Bad WSGI target such as
app:appvswsgi:app→ Gunicorn starts but app import fails → use the correct module and object name. - Missing dependencies in the virtualenv → startup raises
ModuleNotFoundError→ install requirements in the same virtualenv used by the service. - Environment variables not loaded → app crashes during import or config initialization → add
Environment=orEnvironmentFile=to the service. - Service user lacks permission to app files or socket path → Gunicorn cannot read code or create bind target → fix ownership and directory permissions.
- Port already in use or socket path invalid → Gunicorn cannot bind → free the port, remove stale sockets, or fix the bind path.
- Unit file changed without
systemctl daemon-reload→ old configuration still used → reload systemd and restart the service. - Syntax errors or runtime errors in app startup code → Gunicorn exits immediately → test imports manually and review traceback in logs.
- SELinux or restrictive server policy blocks file or socket access → service appears correctly configured but still fails → inspect security policy and file contexts where applicable.
Debugging Section
Use these commands during diagnosis:
sudo systemctl status gunicorn --no-pager -l
sudo journalctl -u gunicorn -n 200 --no-pager
sudo journalctl -xe --no-pager
sudo systemctl cat gunicorn
sudo systemd-analyze verify /etc/systemd/system/gunicorn.service
ls -l /path/to/venv/bin/gunicorn
cd /path/to/app && source /path/to/venv/bin/activate && python -c "from wsgi import app; print(app)"
sudo -u www-data bash -lc 'cd /path/to/app && /path/to/venv/bin/gunicorn --bind 127.0.0.1:8000 wsgi:app'
namei -l /path/to/app
ls -ld /path/to/app /path/to/app/*
ss -ltnp | grep 8000
ss -lx | grep gunicorn
sudo nginx -t
curl -I http://127.0.0.1:8000
What to look for:
ModuleNotFoundErrororImportError→ Flask app import problemPermission denied→ service user cannot access code, virtualenv, or socket directoryNo such file or directory→ bad path inExecStart,WorkingDirectory, or environment fileFailed at step EXECor203/EXEC→ invalid executable path or non-executable binary- address already in use errors → port or socket conflict
- service works manually but not in systemd → unit file environment, user, or working directory mismatch
If the Gunicorn process becomes active but requests still fail, check Nginx upstream configuration and reverse proxy behavior in Fix: Nginx Not Connecting to Gunicorn (Connection Refused).
Checklist
-
gunicornbinary path inExecStartexists and is executable. -
WorkingDirectorypoints to the Flask project root. - WSGI module reference is correct, such as
wsgi:app. - The virtualenv contains Gunicorn and Flask dependencies.
- The systemd service user can read the app files and enter all parent directories.
- Required environment variables are available to the service.
- The configured port or Unix socket can be created and is not already in use.
-
sudo systemctl daemon-reloadwas run after editing the unit file. -
sudo systemctl restart gunicorncompletes successfully. -
systemctl status gunicornshowsactive (running).
For final production verification, use Flask Production Checklist (Everything You Must Do).
Related Guides
- Deploy Flask with Nginx + Gunicorn (Step-by-Step Guide)
- Fix: Nginx Not Connecting to Gunicorn (Connection Refused)
- Flask Production Checklist (Everything You Must Do)
FAQ
Q: What is the fastest way to identify why the Gunicorn service failed?
A: Check systemctl status gunicorn first, then read journalctl -u gunicorn for the exact startup error or traceback.
Q: Why does systemd report the service failed immediately after start?
A: Gunicorn usually exited during app import, failed to execute the configured binary, or could not bind the configured port or socket.
Q: Can a wrong virtualenv cause the service to fail?
A: Yes. If ExecStart points to the wrong environment, Gunicorn or Flask dependencies may be missing.
Q: Do I need to run Gunicorn as the same user configured in systemd?
A: Yes. Manual tests should use the same service user to reproduce permission and environment issues accurately.
Q: Should I debug with a TCP bind before using a Unix socket?
A: Yes. Binding to 127.0.0.1:8000 is often easier to validate during troubleshooting.
Q: What does code=exited, status=203/EXEC mean?
A: systemd could not execute the command in ExecStart, usually because the path is wrong or not executable.
Q: Why does Gunicorn work manually but fail as a service?
A: The service is likely using a different user, working directory, PATH, or environment variable set.
Q: Do I need daemon-reload after changing the unit file?
A: Yes. systemd does not apply unit file changes until you reload its configuration.
Final Takeaway
A failed Gunicorn service is usually caused by a systemd configuration error, an app import failure, or a permission problem. Verify the unit file, test the app manually as the service user, and confirm the bind target and environment before restarting the service.