├── .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 | --------------------------------------------------------------------------------