Nginx

This dead simple application shows you to how to create a simple Docker Image with running FastAPI app with Nginx and presenting the basics of creating and running Docker Images. Each step is deeply described below.

Application structure

|── app
|    ├── __init__.py
|    ├── main.py              # FastAPI app here
|
├── config
|    ├── config.json          # Nginx config file
|
├── .dockerignore             # similar to .gitignore
|                             # but for Docker
├── .gitignore
|
├── Dockerfile                # commands to build
|                             # a Docker Image
├── README.md
|
├── requirements.txt          # pip freeze from
|                             # pip install fastapi

Files description

requirements.txt, .gitignore, .dockerignore

Some common stuff. In .dockerignore I’ve added README.md - we don’t need it in our Container.

app/app/main.py

from fastapi import FastAPI

my_fastapi_app = FastAPI()


@my_fastapi_app.get("/")
async def root():
    return {"Nginx": "I'm alive"}

app/config/config.json

{
    "listeners": {
        "*:80": {
            "pass": "applications/fastapi"
        }
    },

    "applications": {
        "fastapi": {
            "type": "python 3.9",
            "path": "/build/app/",
            "module": "main",
            "callable": "my_fastapi_app"
        }
    }
}

This is a configuration file for our Nginx server. Some basic listener *:80. If we change applications to apps and fastapi to python_app we would have "pass": "apps/python_app".

“type” python 3.9 is obvious because we’re using Docker Image with python version 3.9. Note that we want later in Dockerfile to copy our repo to /build folder inside of the Container so “path” /build/app/ will be a root folder for our FastAPI application. Then “module” is just a python file with application which is main.py so “module” is app.main and since we named the instance of FastAPI class in main.py as a my_fastapi_app, “callable” is my_fastapi_app.

If we had a different folder structure, e.g. one more app folder and then the main.py file, then the “module” would be "app.app.main".

Note

Configuration docs for python apps can be found here: https://unit.nginx.org/configuration/#configuration-python

Dockerfile

FROM nginx/unit:1.23.0-python3.9

# Our Debian with Python and Nginx for python apps.
# See https://hub.docker.com/r/nginx/unit/

COPY ./config/config.json /docker-entrypoint.d/config.json

# Ok, this is something we get thanks to the Nginx Unit Image.
# We don't need to call stuff like
# curl -X PUT --data-binary @config.json --unix-socket \
#       /path/to/control.unit.sock http://localhost/config/
# to set our configuration
# Becouse as stated in docs https://unit.nginx.org/installation/#docker-images,
# configuration snippets are
# uploaded as to the config section of Unit’s configuration
# That means we only have to copy our config.json file to the folder
# /docker-entrypoint.d/

RUN mkdir build

# We create folder named build for our app.

COPY . ./build

# We copy our app folder to the /build

RUN apt update && apt install -y python3-pip                                  \
    && pip3 install -r /build/requirements.txt                               \
    && apt remove -y python3-pip                                              \
    && apt autoremove --purge -y                                              \
    && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/*.list

# OK, that looks strange but here's a explanation from Nginx docs
# https://unit.nginx.org/howto/docker/:

# """ PIP isn't installed by default, so we install it first.
# Next, we install the requirements, remove PIP, and perform image cleanup. """

# Note we use /build/requirements.txt since this is our file

EXPOSE 80

# Instruction informs Docker that the container listens on port 80

Local deployment

In your favourite folder:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
git clone https://github.com/rafsaf/docker-fastapi-projects-nginx.git

cd docker-fastapi-projects-nginx

docker build . -t nginx

# creates image in current folder with tag nginx

docker run --rm -it  -p 80:80/tcp nginx:latest

# runs nginx image

Now the app is up and running locally. In your favourite browser type in:

localhost

You should see:

1
{"Nginx": "I'm alive"}

Awesome!