Hello Coders,
This article explains how to deploy a Flask application sandboxed with a virtualenv
and served by Apache HTTP server using the mod_wsgi
module.
For newcomers, Flask is a lightweight web application framework written in Python. Sometimes classified as a microframework, Flask provides a lightweight codebase that can be easily extended to become an API, a simple web app, or a complex eCommerce platform.
Dependencies
-
Apache
/ httpd (on CentOS) server mod_wsgi
-
Flask
web framework Python3
Virtualenv
Prepare the environment
Install Apache server
$ sudo yum install httpd
$
$ # by default the server is down.
$ sudo systemctl start httpd
Install mod_wsgi
$ sudo yum install mod_wsgi
$
$ # restart apache
$ sudo systemctl restart httpd
Test if the mod_wsgi
module is loaded
$ sudo httpd -M | grep wsgi
wsgi_module (shared) # <-- the OK response
Install Virtualenv
Virtual environments will sandbox the app to run isolated from the global server environment
$ sudo pip install virtualenv
Code the Flask App
We will use a simple Flask application that serves a simple Hello World
message for all routes.
As mentioned before, this setup is executed on CentOs. The steps are:
Go to /var/www
- the www_root of Apache server, and create the project directory.
$ cd /var/www
$ mkdir hitme
To have a runnable Flask app, we need to create two files: run.py
and app.py
inside the app folder. The files are structured as below:
/var/www/hitme
| - run.py
| - app/__init__.py
Where run.py
is responsible to bootstrap the Flask app defined in the app
directory.
app/init.py file contents
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello world!"
run.py file contents
import os
from app import app
#----------------------------------------
# launch
#----------------------------------------
if __name__ == "__main__":
port = int(os.environ.get("PORT", 5000))
app.run(host='0.0.0.0', port=port, debug=True)
Test the Flask App
We have the test application, now let's start it to see something on the screen. First, we need to create and activate the virtual environment.
$ cd /var/www
$ virtualenv --python=python3 hitme # the venv is created inside the app folder
$ cd /var/www/hitme
$ source bin/activate
At this point, we will run the next commands inside the VENV
. Let's install Flask
$ pip install flask
To run, a Flask application require FLASK_APP
environment variable.
$ export FLASK_APP=run.py # please notice the name
$ flask run # start the app
$ # our app is running on port 5000
Apache Configuration
To execute a Flask application under the Apache HTTP server we need to bridge our application to the Apache engine using the mod_wsgi module. For this we need to create a new file wsgi.py
inside our project folder:
/var/www/hitme
| - wsgi.py
| - run.py
| - app/__init__.py
wsgi.py file contents
#!/usr/bin/env python
import sys
import site
site.addsitedir('/var/www/hitme/lib/python3.6/site-packages')
sys.path.insert(0, '/var/www/hitme')
from app import app as application
The next step is to configure Apache to serve the app and use this wsgi
loader. The following settings should be added to the httpd.conf
. On CentOS the file location is /etc/httpd/conf/httpd.conf
<VirtualHost *:80>
ServerName localhost
WSGIDaemonProcess hitme user=apache group=apache threads=2
WSGIScriptAlias / /var/www/hitme/wsgi.py
<Directory /var/www/hitme>
Require all granted
</Directory>
</VirtualHost>
Close and save the file and restart Apache to load the new settings.
$ # restart apache
$ sudo systemctl restart httpd
Our Flask app should be served by the Apache HTTP server. We can test the deploy by using lynx
command:
$ lynx localhost # lynx
Thanks for reading! For more resources, feel free to access:
- 👉 More Flask Dashboards crafted in Django, Flask, and
React
- ✨ More Flask Apps - free & PAID
Top comments (34)
Hi.. Thanks for this tutorial.. I tried it line by line and it worked. However, I've been trying to deploy a more complicated flask app that I have been working on for a while, and it's not working. I hope you can help me out. I'll be very grateful.
Hello Abdul, Thanks for reading.
I'll be glad to help. Tell me more about your app.
Hi,
It's an app about running warehouses and simple accounting and invoices. It has a lot of local imports. I originally placed the "run.py" inside the project folder and it worked perfectly on the development server, but that does not match the structure described in your tutorial and it would not deploy.
When I place the "run.py" outside the project folder, it would not recognize the local packages I'm importing from within the project.
If you like to take a better look... here's the link on github
github.com/abbddos/Enterprise_1
This is my first web project ever, so I know there's a small twist somewhere that I'm missing.
Thank you very very much
Hello,
let's chat in private.
I tried the steps above, but stuck during "flask run" with below error, Can anyone please help / point out where I'm going wrong ?
Error: While importing 'run', an ImportError was raised:
Traceback (most recent call last):
File "/var/www/hitme/lib/python3.6/site-packages/flask/cli.py", line 260, in locate_app
import(module_name)
File "/var/www/hitme/run.py", line 2, in
from app import app
ImportError: cannot import name 'app'
I was able to work through this and resolve this by using
import web_tool
app = web_tool.app
@sm0ke Thanks for this tutorial! Very helpful!
For anybody referencing a a virtual environment in their wsgi file, and getting Permission Denied in the httpd error logs:
Make sure proper permissions are applied to the virtual environment's parent directories, so that the apache user, or whatever user you have set for the WSGIDaemonProcess, can actually get to it.
Hello Xavier. If you have the time, please drop here a config sample that uses WSGIDaemonProcess.
10x mate
I used the same config as your tutorial where the WSGIDaemonProcess will be ran as the apache user.
Super cool. Thank you! :)
Hi,
I Followed your steps from the above tutorial without using lynx, directly used browser and given localhost:8080 but browser not giving Hello world and showing that site cant be reached. restarted Apache server, port 8080 is listening and open,even wsgi file is also same like yours. below are the logs. pls check it out
[Wed Mar 25 16:26:18.575220 2020] [core:notice] [pid 7322:tid 139847942441216] AH00094: Command line: '/usr/sbin/httpd -D FOREGROUND'
[Wed Mar 25 17:59:57.166193 2020] [wsgi:error] [pid 7326:tid 139847205377792] [client 162.243.129.144:49818] Target WSGI script not found or unable to stat: /var/www/flask/wsgi.py
Hello Sirisha,
Thanks for noticing the article. Try to apply this short checklist:
Please notice that Apache runs under certain privileges. Make sure that Apache can access the file.
Let me know if my suggestions helped.
yes, exists on disk and given permission using this command: chmod 755 /var/www/flask/wsgi.py.. but no change
I have followed your steps from the above tutorial and one of the problem is that i cannot specifying my python interpreter to be the vitual one, and apache just uses the default python2.7.5 initially installed in centos.how can i solve this
[Thu Apr 09 13:42:29.349530 2020] [lbmethod_heartbeat:notice] [pid 16236] AH02282: No slotmem from mod_heartmonitor
[Thu Apr 09 13:42:29.353249 2020] [mpm_prefork:notice] [pid 16236] AH00163: Apache/2.4.6 (CentOS) mod_wsgi/3.4 Python/2.7.5 configured -- resuming normal operations
[Thu Apr 09 13:42:29.353276 2020] [core:notice] [pid 16236] AH00094: Command line: '/usr/sbin/httpd -D FOREGROUND'
[Thu Apr 09 13:42:32.225160 2020] [:error] [pid 16242] [client 120.230.96.144:49562] mod_wsgi (pid=16242): Target WSGI script '/var/www/hitme/wsgi.py' cannot be loaded as Python module.
[Thu Apr 09 13:42:32.225201 2020] [:error] [pid 16242] [client 120.230.96.144:49562] mod_wsgi (pid=16242): Exception occurred processing WSGI script '/var/www/hitme/wsgi.py'.
[Thu Apr 09 13:42:32.225224 2020] [:error] [pid 16242] [client 120.230.96.144:49562] Traceback (most recent call last):
[Thu Apr 09 13:42:32.225243 2020] [:error] [pid 16242] [client 120.230.96.144:49562] File "/var/www/hitme/wsgi.py", line 13, in
[Thu Apr 09 13:42:32.225307 2020] [:error] [pid 16242] [client 120.230.96.144:49562] from app import app as application
[Thu Apr 09 13:42:32.225318 2020] [:error] [pid 16242] [client 120.230.96.144:49562] File "/var/www/hitme/app/init.py", line 2, in
[Thu Apr 09 13:42:32.225455 2020] [:error] [pid 16242] [client 120.230.96.144:49562] from flask import Flask
[Thu Apr 09 13:42:32.225469 2020] [:error] [pid 16242] [client 120.230.96.144:49562] File "/var/www/hitme/hitme/lib/python3.7/site-packages/flask/init.py", line 14, in
[Thu Apr 09 13:42:32.225511 2020] [:error] [pid 16242] [client 120.230.96.144:49562] from jinja2 import escape
[Thu Apr 09 13:42:32.225519 2020] [:error] [pid 16242] [client 120.230.96.144:49562] File "/var/www/hitme/hitme/lib/python3.7/site-packages/jinja2/init.py", line 9, in
[Thu Apr 09 13:42:32.225561 2020] [:error] [pid 16242] [client 120.230.96.144:49562] from .bccache import BytecodeCache
[Thu Apr 09 13:42:32.225570 2020] [:error] [pid 16242] [client 120.230.96.144:49562] File "/var/www/hitme/hitme/lib/python3.7/site-packages/jinja2/bccache.py", line 19, in
[Thu Apr 09 13:42:32.225649 2020] [:error] [pid 16242] [client 120.230.96.144:49562] from .compat import BytesIO
[Thu Apr 09 13:42:32.225658 2020] [:error] [pid 16242] [client 120.230.96.144:49562] File "/var/www/hitme/hitme/lib/python3.7/site-packages/jinja2/_compat.py", line 120, in
[Thu Apr 09 13:42:32.225701 2020] [:error] [pid 16242] [client 120.230.96.144:49562] from pathlib import PurePath
[Thu Apr 09 13:42:32.225709 2020] [:error] [pid 16242] [client 120.230.96.144:49562] File "/usr/local/Python-3.7.7/Lib/pathlib.py", line 9, in
[Thu Apr 09 13:42:32.226042 2020] [:error] [pid 16242] [client 120.230.96.144:49562] from _collections_abc import Sequence
[Thu Apr 09 13:42:32.226086 2020] [:error] [pid 16242] [client 120.230.96.144:49562] File "/usr/local/Python-3.7.7/Lib/_collections_abc.py", line 58
[Thu Apr 09 13:42:32.226092 2020] [:error] [pid 16242] [client 120.230.96.144:49562] async def _coro(): pass
[Thu Apr 09 13:42:32.226095 2020] [:error] [pid 16242] [client 120.230.96.144:49562] ^
[Thu Apr 09 13:42:32.226098 2020] [:error] [pid 16242] [client 120.230.96.144:49562] SyntaxError: invalid syntax
[Thu Apr 09 13:42:35.936716 2020] [:error] [pid 16243] [client 120.230.96.144:49564] mod_wsgi (pid=16243): Target WSGI script '/var/www/hitme/wsgi.py' cannot be loaded as Python module.
[Thu Apr 09 13:42:35.936795 2020] [:error] [pid 16243] [client 120.230.96.144:49564] mod_wsgi (pid=16243): Exception occurred processing WSGI script '/var/www/hitme/wsgi.py'.
[Thu Apr 09 13:42:35.936830 2020] [:error] [pid 16243] [client 120.230.96.144:49564] Traceback (most recent call last):
[Thu Apr 09 13:42:35.936861 2020] [:error] [pid 16243] [client 120.230.96.144:49564] File "/var/www/hitme/wsgi.py", line 13, in
[Thu Apr 09 13:42:35.936955 2020] [:error] [pid 16243] [client 120.230.96.144:49564] from app import app as application
[Thu Apr 09 13:42:35.936972 2020] [:error] [pid 16243] [client 120.230.96.144:49564] File "/var/www/hitme/app/init.py", line 2, in
[Thu Apr 09 13:42:35.937159 2020] [:error] [pid 16243] [client 120.230.96.144:49564] from flask import Flask
[Thu Apr 09 13:42:35.937179 2020] [:error] [pid 16243] [client 120.230.96.144:49564] File "/var/www/hitme/hitme/lib/python3.7/site-packages/flask/init.py", line 14, in
[Thu Apr 09 13:42:35.937246 2020] [:error] [pid 16243] [client 120.230.96.144:49564] from jinja2 import escape
[Thu Apr 09 13:42:35.937261 2020] [:error] [pid 16243] [client 120.230.96.144:49564] File "/var/www/hitme/hitme/lib/python3.7/site-packages/jinja2/init_.py", line 9, in
[Thu Apr 09 13:42:35.937313 2020] [:error] [pid 16243] [client 120.230.96.144:49564] from .bccache import BytecodeCache
[Thu Apr 09 13:42:35.937361 2020] [:error] [pid 16243] [client 120.230.96.144:49564] File "/var/www/hitme/hitme/lib/python3.7/site-packages/jinja2/bccache.py", line 19, in
[Thu Apr 09 13:42:35.937490 2020] [:error] [pid 16243] [client 120.230.96.144:49564] from ._compat import BytesIO
[Thu Apr 09 13:42:35.937505 2020] [:error] [pid 16243] [client 120.230.96.144:49564] File "/var/www/hitme/hitme/lib/python3.7/site-packages/jinja2/_compat.py", line 120, in
[Thu Apr 09 13:42:35.937589 2020] [:error] [pid 16243] [client 120.230.96.144:49564] from pathlib import PurePath
[Thu Apr 09 13:42:35.937606 2020] [:error] [pid 16243] [client 120.230.96.144:49564] File "/usr/local/Python-3.7.7/Lib/pathlib.py", line 9, in
[Thu Apr 09 13:42:35.938027 2020] [:error] [pid 16243] [client 120.230.96.144:49564] from _collections_abc import Sequence
[Thu Apr 09 13:42:35.938093 2020] [:error] [pid 16243] [client 120.230.96.144:49564] File "/usr/local/Python-3.7.7/Lib/_collections_abc.py", line 58
[Thu Apr 09 13:42:35.938104 2020] [:error] [pid 16243] [client 120.230.96.144:49564] async def _coro(): pass
[Thu Apr 09 13:42:35.938109 2020] [:error] [pid 16243] [client 120.230.96.144:49564] ^
[Thu Apr 09 13:42:35.938114 2020] [:error] [pid 16243] [client 120.230.96.144:49564] SyntaxError: invalid syntax
this is the error caused by python2.x in my app
Oh .. , sounds like super fun. I see two options here:
Let me know if this helps.
I have gone with the first approach and encounters problems and would like to communicate with you on this a bit more.
so i uninstall mod_wsgi on python2, and tried install using zip pack from mod_wsgi website
./configure --with-python=/usr/local/bin/python3 this is the configure that i used to specify python interpreter
then make && make install
problem occurs(part of the error reports looks like the following)
my server is a centos 7 with gcc and python2 installed already. i install python3 using zip pack from its home webside and set --enable-shared to make sure that its library can be shared.
another way i tried is through pip3 install mod_wsgi
problems:
i have stuck with this for a few days and would really appreciated if you can help
thx sincerely.
Hello,
That's a super heavy error. I've googled a little, nothing relevant found. Apache mod_wsgi is super buggy on CentOS. I had many issues in the past and skip over that setup.
I'm using on CentOS a simple setup that uses uWSGI to start the Flask/Django app and Apache as a reverse proxy.
uWSGI is fast but is not handle the SSL layer (that's why you need Apache in front). Feel free to use Gunicorn instead of uWSGI, in case you are familiar with it.
I'm not super free with my time, but we can chat LIVE on Discord. I'm Sm0ke on that server (the moderator).
Hi Sm0ke, Thank you for this great article. It is very helpful and well written.
I noticed what I believe is one small typo. Where it says "app/init.py file contents" I believe it should say "app/init.py file contents", and that is probably a Markdown issue.
Were you able to resolve @williamium3000 's issues regarding setting the python interpreter? I'm trying to get this to work with pyenv (pyenv install 3.9.5), but can't get mod_wsgi to see my locally installed interpreter in my venv, even if I put it in the she-bang
Reports:
3.6.8 is the OS's python version. Thanks for your help
Hello @dlink ,
Indeed the
app/\_\_init\_\_.py
cannot be formatted being styled automatically by the Markdown parser.Regarding the Python version, try to force the PATH update in wsgi.py as below
P.S. adapt the paths to be resolved properly.
Let me know the results.
Hi, thanks for this tutorial dude, I'm following the tutorial and work fine but with personal project not work and send me error "Target WSGI script .... cannot be loaded as python module". Any idea what happend? thank you
Hi!
I am on flask run part, in console it shows:
Serving Flask app "run.py" (lazy loading)
Thanks for this tutorial.
I have a problem, WSGT cannot load Python module. I checked to error_log
Do you have any idea to resolve this problem?
hi, I got a 403 forbidden error, any possible solutions? I created my app not in www folder, is this going to cause a trouble.
Hello Chase. Thank you for noticing the article.
Can you extract drop here some relevant information from the apache logs?
Also, using a Heroku deploy, might be a good choice.
Hi, I have figured out the problem, all I have to do is to create every in var/www folder instead. But I started to have another problem is that, the system try to run the app using python2.7 which is absolutely causing errors.
Here is the error log gives me,
[Thu Feb 06 23:17:27.417817 2020] [:error] [pid 24709] [client ::1:39996] SyntaxError: invalid syntax
[Thu Feb 06 23:20:07.161239 2020] [:error] [pid 24708] [client ::1:39998] mod_wsgi (pid=24708): Target WSGI script '/var/www/nzn/wsgi.py' cannot be loaded as Python module.
[Thu Feb 06 23:20:07.161274 2020] [:error] [pid 24708] [client ::1:39998] mod_wsgi (pid=24708): Exception occurred processing WSGI script '/var/www/nzn/wsgi.py'.
[Thu Feb 06 23:20:07.161288 2020] [:error] [pid 24708] [client ::1:39998] Traceback (most recent call last):
[Thu Feb 06 23:20:07.161301 2020] [:error] [pid 24708] [client ::1:39998] File "/var/www/nzn/wsgi.py", line 10, in
[Thu Feb 06 23:20:07.161344 2020] [:error] [pid 24708] [client ::1:39998] from nzn import app as application
[Thu Feb 06 23:20:07.161372 2020] [:error] [pid 24708] [client ::1:39998] File "/var/www/nzn/nzn/init.py", line 64
[Thu Feb 06 23:20:07.161376 2020] [:error] [pid 24708] [client ::1:39998] marks = {range: f'{range:.2f}' for range in [0.02, 0.05, 0.1, 0.15, 0.2]},
[Thu Feb 06 23:20:07.161379 2020] [:error] [pid 24708] [client ::1:39998] ^
[Thu Feb 06 23:20:07.161381 2020] [:error] [pid 24708] [client ::1:39998] SyntaxError: invalid syntax
As my personal experience this error is caused by using the python2 instead of python3.
Thanks
Hello, yep might be the Python version.
Using Python3 is not an option? Python2 reach the EOL in Jan this year.