Minimal FastAPI application deployed on a Docker Swarm cluster.
For demonstration purposes we're going to provision virtual machines using Lima to set the Docker Swarm cluster on multiple nodes. You can use any other virtualization tools such as VirtualBox, VMWare etc. The goal of the project is to provide a straightforward way to deploy and maintain a minimal FastAPI application on a Docker Swarm cluster.
- About
- Prerequisites
- System Requirements
- Project Features
- Project Structure
- Docker Swarm Cluster Setup
- Configuration
- Deployment
- CLI Snippets
- Further Steps
- References
- Python >=3.12,<3.13
- Docker v1.12+ (for Docker Swarm Mode support)
- (Optional) Virtualization Tool such as Lima/VirtualBox/VMWare etc.
- FastAPI + Pydantic
- Alembic for managing database migrations
- JSON logging
- Ruff for linting, Ty for type checking
- Pytest for testing
- Pre-commit hooks to perform actions (formatting, testing etc.) before pushing code to the repository
- Deploy to Docker Swarm multi-node cluster
- Configs and Secrets management using Docker capabilities
- VMs provisioning with Lima
- Integration with Portainer for Docker Swarm cluster monitoring
.
├── assets
│ └── images # Project images and diagrams for documentation
├── configs
│ ├── api.env # Docker Config providing application configuration
│ ├── uvicorn.env # Configuration for Uvicorn (serves the application)
│ ├── api.logging.config.json # Logging configuration file for the application
├── secrets
│ ├── db_password.txt # Docker Secret to store the database password
├── src
│ ├── alembic # Configuration for Alembic. Database migrations and files
│ │ ├── ...
│ ├── app
│ │ ├── main.py # Main FastAPI application entrypoint
│ │ ├── core # Core application providing shared resources such as settings, schemas, models etc.
│ │ │ ├── ...
│ │ └── users # Application to manage users on the API level
│ │ ├── ...
│ └── tests # Project tests
│ ├── ...
├── compose.yml # Compose file to deploy the application to Docker Swarm cluster
├── lima.sh # Custom script to provision virtual machine for Docker Swarm deployment
├── Dockerfile # Project Docker image builder
├── Makefile # Project Management commands
├── .env # File to dynamically configure compose services
├── .pre-commit-config.yaml # Pre commit hooks
├── pyproject.toml # Project configuration and dependencies management
└── uv.lock # Locked project management file
├── README.md # Project documentationBasic architecture for our Docker Swarm cluster is
Note: skip this step if you don't use Lima for provisioning VMs.
sudo chmod +x ./lima.sh
./lima.sh manager,worker1,worker2,db,cacheInitialize Docker Swarm cluster on the Manager node:
limactl shell manager docker swarm initCopy the join token and address of the Manager node from the output into variables for further steps:
TOKEN=<docker-swarm-join-token>
MANAGER_ADDR=<manager-node-address>limactl shell worker1 docker swarm join --token $TOKEN $MANAGER_ADDR
limactl shell worker2 docker swarm join --token $TOKEN $MANAGER_ADDR
limactl shell db docker swarm join --token $TOKEN $MANAGER_ADDR
limactl shell cache docker swarm join --token $TOKEN $MANAGER_ADDRList cluster nodes:
limactl shell manager docker node lsThere are 2 labels:
- tag - label for tagging nodes to deploy specific services on;
- type - general label used for Portainer to deploy the agent on each node of the stack.
# connect to the Manager node
limactl shell manager
# label nodes
docker node update --label-add type=fastapi-stack --label-add tag=manager lima-manager
docker node update --label-add type=fastapi-stack --label-add tag=worker lima-worker1
docker node update --label-add type=fastapi-stack --label-add tag=worker lima-worker2
docker node update --label-add type=fastapi-stack --label-add tag=db lima-db
docker node update --label-add type=fastapi-stack --label-add tag=cache lima-cacheConfiguration of the entire stack is based on:
- environment variables - for configuring Docker Swarm services;
- Docker Secrets - for managing sensitive information such as passwords;
- Docker Configs - for managing regular configuration of the application.
There are 4 regular configuration files:
- .env - environment-based config for compose.yml services;
- configs/api.env - environment-based config for the web application;
- configs/uvicorn.env - environment-based config for Uvicorn (runs the web application);
- configs/api.logging.config.json - JSON-based logging config for the web application.
And 1 config to store sensitive secret information:
- secrets/db_password.txt
Run the following command to generate regular configuration files from their corresponding templates:
make configNote: make config command doesn't override the configuration files if any already exists.
Run the following command to generate configs to store sensitive secret information:
make secretsNote: make secrets command overwrites files with new values.
Once all the configuration files are set, deploy the stack.
Deploy the application stack on the Manager node:
limactl shell manager make stack-deployOnce the stack is deployed, you can check its status:
limactl shell manager stack-status
# or
limactl shell manager stack-servicesIf everything's ok, you can ping the API health like this:
limactl shell manager curl http://127.0.0.1:8000/api/healthDuring development you might want to make the application accessible from the host machine. Follow the steps below to achieve this:
Step 1. Create a tunnel for the Manager node:
limactl tunnel managerThe command will output the proxy address (the port is randomly assigned):
Set `ALL_PROXY=socks5h://127.0.0.1:<PORT>`, etc.
The instance can be connected from the host as <http://lima-default.internal> via a web browser.
Step 2. Access the application from your host machine:
curl --proxy socks5h://127.0.0.1:<PORT> http://lima-manager.internal:8000/api/healthStep 3 (optional). In order to open the application in the browser you have to configure SOCKS5 proxy at 127.0.0.1:, then navigate to http://lima-manager.internal:8000/api/health.
The image below shows SOCKS5 proxy configuration for Firefox.
Note that 40333 is the port that was provided by limactl tunnel command.
You can also access the Portainer's UI from the browser of your host machine at https://lima-manager.internal:9443 to monitor your Docker Swarm cluster.
The commands below might be helpful during local development:
# list the available project commands
make help
# generate new database migrations file
make migrations m="<some-message>"
# apply the database migrations
make migrate
# run checks such as Ruff for linting, Ty for type checking, Pytest for testing
make check
# run the API locally
make run RELOAD=1
# clean internal cached files and directories
make clean-
Add a separate service to provide Admin UI for the FastAPI application.
-
Add proxy service such as Nginx or Traefik.
-
Add basic CI/CD pipeline using Github Actions.
-
...

