How to deploy any Python Web Application?
Tags: python, webdev, nginx, hypercorn
Published: 2021-11-14 18:17:01

*Hey everyone👨‍💻*
In this blog post I will explain how you can deploy any *ASGI/WSGI compliant Python Web App.*

####DISCLAIMER:
___Only ASGI Compliant Frameworks can be deployed using this method, other frameworks can't be deployed.___

###List of Tools I will be using:
- NGINX
- Hypercorn
- FastAPI


###__Now here, there are alternatives to Hypercorn and FastAPI__
####Alternatives to Hypercorn:
 - Gunicorn
 - Uvicorn
 - Daphne


####Other Frameworks that can be deployed:
 - Flask
 - Django
 - Starlette
 - *Any ASGI/WSGI compliant framework*


###Step One:
Setup your framework using the docs mentioned.

- [Django](docs.djangoproject.com/)
- [FastAPI](fastapi.tiangolo.com/)
- [Flask](https://flask.palletsprojects.com/en/2.0.x/)

Since I'll be using FastAPI, my `main.py` looks like this
```py
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def hello_world():
   return f"Hello World"
```



🚀 We now have a FastAPI app ready, we now have to deploy it using *NGINX*. ⚙️


###Step Two:

*Depending upon your framework and choice of ASGI/WSGI Server, this process will be slightly different.*

<u>For Django Devs:</u> 

Your wsgi/asgi application would be called as `<application_name>.<a/w>sgi:application`
__Choose ASGI or WSGI clearly and stay with that option throughout__

<u> For Flask Devs:</u> 

If your app is in main.py, it would be called as `main:app`


In this step we'll be binding the web-server to UNIX socket. Learn more about UNIX Sockets. [here](https://www.ibm.com/docs/en/ztpf/1.1.0.15?topic=considerations-unix-domain-sockets)

I am attaching the docs of Daphne, Uvicorn and Gunicorn down which use different flags to bind the application to a port.

Run this command to bind it to the socket
```sh
hypercorn -b 'unix:/var/tmp/hypercorn.sock' -w 4 main:app
```

In this `-w` defines the number of workers.
Change `hypercorn.sock` to the server which you choose to use.

**Change the socket name according to your web server**

🎇 Now we have our app listening on the `hypercorn.sock`.

###Step Three:
We've to proxy this socket to *NGINX* and route *NGINX* to listen to the `hypercorn` socket.

```nginx
worker_processes 1;
events {
  worker_connections 512;
}
http {
  server {
    listen 8080;
    server_name "localhost";
    access_log /var/log/nginx/access.log;
    error_log /var/log/error.log ;
    location / {
      proxy_pass http://unix:/var/tmp/hypercorn.sock;
    }
   }
}
```
I'll briefly explain this config file:
 - Worker_processes => 1 worker process has been assigned for this specific task/process
 - Worker connections => Number of connections that can be handled by 1 process
 - Listen => Listens at the mentioned port
 - Server Name => Listens at this domain
 - Access_log => The file location at which access log is stored, access log stores requests made
 - Error_log => The file location at which error log is stored.
 - Proxy Pass => The socket/port which needs to be proxied.

**This file should change based on your socket but the other configuration can be the same.**

🚅 Save this file as nginx.conf

Feel free to read about *NGINX* [here](https://nginx.org/en/docs/)

Once this file is made, save it at `/etc/nginx/`

Either you can use docker to run a Linux server or shell into an instance.

If you want to copy it to docker.
```dockerfile
COPY nginx.conf /etc/nginx/
```

💣 You are ready to launch except one last step

###Step four

 - You have now wonderfully setup your web-server and the NGINX proxy 🙌
 - You are just one-step away from accessing the port, and perhaps this is the ✅ or ❌ step

Currently, NGINX can't read or write from the socket, so we need to `change access mode`

To do this, run the following command:

```sh
chmod 777 /var/tmp/<socket> 
sudo service nginx restart
```

🌟Now you can listen from the port 8080, `http://localhost:8080`

If you are using systemctl, please use this command instead:

```sh
sudo systemctl restart nginx
```

*Play around with NGINX config as you wish based on your application's requirements.*

Thanks for reading🧑‍🚀



######Docs:
 - [Uvicorn](https://www.uvicorn.org/deployment/#running-behind-nginx)
 - [Gunicorn](https://docs.gunicorn.org/en/stable/deploy.html)
 - [Daphne](https://github.com/django/daphne)