├── .editorconfig
├── .gitignore
├── Makefile
├── README.md
├── myproject.py
├── requirements.txt
├── static
└── my-asset.html
└── wsgi.py
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 |
7 | [Makefile]
8 | indent_style = tab
9 |
10 | [*.md]
11 | trim_trailing_whitespace = false
12 |
13 | [*.py]
14 | charset = utf-8
15 | indent_size = 4
16 | indent_style = space
17 | trim_trailing_whitespace = true
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # macOS
2 | .DS_Store
3 |
4 | # PyCharm
5 | .idea
6 |
7 | # virtualenv
8 | venv
9 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | help:
2 | @echo
3 | @echo "🍶 FLASK"
4 | @echo
5 | @echo "*flask-serve*: start server"
6 | @echo "*flask-hit*: hit server"
7 | @echo
8 | @echo "🦄 GUNICORN"
9 | @echo
10 | @echo "*guni-serve*: start server"
11 | @echo "*guni-hit*: hit server"
12 | @echo
13 | @echo "❎ NGINX"
14 | @echo
15 | @echo "*ng-serve*: start server"
16 | @echo "*ng-hit*: hit server"
17 | @echo "*ng-hit-static*: hit static assets"
18 | @echo "*ng-up*: reload configuration"
19 | @echo "*ng-quit*: quit"
20 | @echo
21 | @echo "📦 DEPENDENCIES"
22 | @echo
23 | @echo "*pipin*: install dependencies from requirements.txt"
24 | @echo
25 |
26 | flask-serve:
27 | python3 myproject.py
28 |
29 | flask-hit:
30 | http http://127.0.0.1:5000
31 |
32 | guni-serve:
33 | gunicorn --bind 0.0.0.0:8000 wsgi
34 |
35 | guni-hit:
36 | http http://127.0.0.1:8000
37 |
38 | ng-serve:
39 | nginx
40 |
41 | ng-hit:
42 | http http://127.0.0.1:8080
43 |
44 | ng-hit-static:
45 | http http://127.0.0.1:8080/static/
46 |
47 | ng-up:
48 | nginx -s reload
49 |
50 | ng-quit:
51 | nginx -s quit
52 |
53 | pipin:
54 | pip install -r requirements.txt
55 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OVERVIEW
2 |
3 | Most guides show how to set up servers on a Linux box (which makes sense) but macOS also works if you don't have a Linux box handy.
4 |
5 | I've also posted this on [Stack Overflow](https://stackoverflow.com/a/54298517) in very condensed form.
6 |
7 | # WHAT WE'LL DO
8 |
9 | ```language
10 | +---------------+
11 | | Nginx |
12 | | |
13 | | |
14 | +---------------+
15 | X
16 | X
17 | X
18 | X
19 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
20 | X X
21 | X X
22 | X X
23 | +----------------+ X
24 | | gunicorn | X
25 | | | X
26 | | | +----------------+
27 | +----------------+ | static |
28 | X | assets |
29 | X | |
30 | +----------------+ +----------------+
31 | | Flask |
32 | | |
33 | | |
34 | +----------------+
35 | ```
36 |
37 | # THINGS WE'LL USE
38 |
39 | * packages from Homebrew: `nginx` and `httpie`
40 | * Python 3
41 | * Makefile for our commands (to understand what all the commands do, just run `make help`)
42 | * Flask for the webserver (our gunicorn and Nginx config should hold true for any WSGI server -> if you try this with Django, Pyramid, et al. and run into a problem, just open an issue and we'll figure it out!)
43 |
44 | # STEP 1: NGINX ➡️ STATIC ASSETS
45 |
46 | ```language
47 | +---------------+
48 | | Nginx |
49 | | |
50 | | |
51 | +---------------+
52 | X
53 | X
54 | X
55 | X
56 | XXXXXXXXXXXXXXX
57 | X
58 | X
59 | X
60 | X
61 | X
62 | X
63 | +----------------+
64 | | static |
65 | | assets |
66 | | |
67 | +----------------+
68 |
69 | ```
70 |
71 | * download Nginx using Homebrew: `brew install nginx`
72 | * start Nginx: `make ng-serve`
73 | * hit Nginx: `make ng-hit` to hit our Nginx server, which will return its default welcome page
74 |
75 | ```sh
76 | $ make ng-hit
77 |
78 | http http://127.0.0.1:8080
79 | HTTP/1.1 200 OK
80 | # other output
81 |
Welcome to nginx!
82 | ```
83 |
84 | * next, go update the Nginx configuration file (`/etc/nginx/nginx.conf`) to point at our repo's static assets
85 |
86 | ```diff
87 | - location / {
88 | + location /static {
89 | - root html;
90 | + root path/to/your/nginx-wsgi;
91 | - index index.html index.htm;
92 | + index my-asset.html;
93 | }
94 | ```
95 |
96 | * reload Nginx so it knows about our updated config: `make ng-up`
97 | * hit Nginx at the `static` route to validate it's serving our bespoke HTML 😄: `make ng-hit-static`
98 |
99 | ```sh
100 | $ make ng-hit-static
101 |
102 | http http://127.0.0.1:8080/static/
103 | HTTP/1.1 200 OK
104 | # other output
105 | Nginx is serving my-asset.html!
106 | ```
107 |
108 | # STEP 2: GUNICORN ➡️ FLASK
109 |
110 | ```language
111 | +----------------+
112 | | gunicorn |
113 | | |
114 | | |
115 | +----------------+
116 | X
117 | X
118 | +----------------+
119 | | Flask |
120 | | |
121 | | |
122 | +----------------+
123 | ```
124 |
125 | * [make a virtual environment and activate it](https://realpython.com/python-virtual-environments-a-primer/), then install the dependencies: `make pipin`
126 | * start Flask: `make flask-serve`
127 | * hit Flask on port 5000: `make flask-hit`
128 |
129 | ```sh
130 | $ make flask-hit
131 |
132 | http http://127.0.0.1:5000
133 | Flask running!
134 | ```
135 |
136 | * start gunicorn: `make guni-serve`
137 | * hit gunicorn on port 8000 to validate that it passes the request to Flask on port 5000: `make guni-hit`
138 |
139 | ```diff
140 | $ make guni-hit
141 |
142 | http http://127.0.0.1:8000
143 | Flask running!
144 | ```
145 |
146 | # STEP 3: NGINX ➡️ GUNICORN
147 |
148 | ```language
149 | +---------------+
150 | | Nginx |
151 | | |
152 | | |
153 | +---------------+
154 | X
155 | X
156 | X
157 | X
158 | XXXXXXXXXXXXXXXXX
159 | X
160 | X
161 | X
162 | +----------------+
163 | | gunicorn |
164 | | |
165 | | |
166 | +----------------+
167 | ```
168 |
169 | * update the Nginx conf again, this time to pass requests to gunicorn
170 |
171 | ```diff
172 | + location / {
173 | + proxy_pass http://127.0.0.1:8000;
174 | + }
175 | ```
176 |
177 | * reload Nginx so it knows about our updated config: `make ng-up`
178 | * hit Nginx: `make ng-hit` -> this time, instead of the default Nginx page, we'll see that the request passes from Nginx to gunicorn and finally to Flask
179 |
180 | ```sh
181 | $ make ng-hit
182 |
183 | http http://127.0.0.1:8080
184 | HTTP/1.1 200 OK
185 | # other output
186 | Flask running!
187 | ```
188 |
189 | ---
190 |
191 | Now, everything is wired together:
192 |
193 | ```language
194 | +---------------+
195 | | Nginx |
196 | | |
197 | | |
198 | +---------------+
199 | X
200 | X
201 | X
202 | X
203 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
204 | X X
205 | X X
206 | X X
207 | +----------------+ X
208 | | gunicorn | X
209 | | | X
210 | | | +----------------+
211 | +----------------+ | static |
212 | X | assets |
213 | X | |
214 | +----------------+ +----------------+
215 | | Flask |
216 | | |
217 | | |
218 | +----------------+
219 | ```
220 |
221 | This guide is just to get you up-and-running. For more explanation, here are some articles that helped me from three guys named [Cheng](http://cheng.logdown.com/posts/2015/01/29/deploy-django-nginx-gunicorn-on-mac-osx-part-2), [Honza](http://honza.ca/2011/05/deploying-django-with-nginx-and-gunicorn), and [Patrick]( https://www.patricksoftwareblog.com/how-to-configure-nginx-for-a-flask-web-application/)
222 |
--------------------------------------------------------------------------------
/myproject.py:
--------------------------------------------------------------------------------
1 | from flask import Flask
2 | application = Flask(__name__)
3 |
4 |
5 | @application.route("/")
6 | def hello():
7 | return "Flask running
"
8 |
9 |
10 | if __name__ == "__main__":
11 | application.run(host='0.0.0.0')
12 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | Click==7.0
2 | Flask==2.3.2
3 | gunicorn==23.0.0
4 | itsdangerous==1.1.0
5 | Jinja2==3.1.6
6 | MarkupSafe==1.1.0
7 | Werkzeug==3.0.6
8 |
--------------------------------------------------------------------------------
/static/my-asset.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | my-asset-page
4 |
5 |
6 | Nginx is serving my-asset.html!
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/wsgi.py:
--------------------------------------------------------------------------------
1 | from myproject import application
2 |
3 | if __name__ == "__main__":
4 | application.run()
5 |
--------------------------------------------------------------------------------