In this post, we are going to set up a domain for our web app and also provide HTTPS support for better security. Let's add some volume mapping and 443 port mapping in the nginx service and add a new service called certbot in docker-compose-prod.yml
version: '3.9'
services:
web:
build:
context: ./
dockerfile: Dockerfile
ports:
- "8000:8000"
environment:
- ENV_VAR_NAME=VALUE
volumes:
- ./:/app
command: uvicorn main:app --host 0.0.0.0 --port 8000 --reload
nginx:
image: nginx:latest
ports:
- "80:80"
- "443:443" #new
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/certbot/www:/var/www/certbot/:ro #new
- ./nginx/certbot/conf/:/etc/nginx/ssl/:ro #new
depends_on:
- web
restart: always
#new
certbot:
image: certbot/certbot:latest
command: certonly --webroot --webroot-path=/var/www/certbot --force-renewal --email [email protected] --agree-tos --no-eff-email -d despam.io
volumes:
- ./nginx/certbot/www/:/var/www/certbot/:rw
- ./nginx/certbot/conf/:/etc/letsencrypt/:rw
NGINX Service: Exposes ports 80 and 443 for HTTP and HTTPS traffic respectively. volumes
: Mounts directories from your host machine into the Nginx container. By defining the volumes we are creating a mapping, Whatever we put in these volumes will be pushed in the docker container. If we put something in the docker container it will appear in the host Virtual machine. :ro
at the end of the volume declaration. ro
means "read-only". The container will never have the right to update a file in this folder.
Certbot Service: certonly
: Requests and obtains SSL/TLS certificates. --webroot --webroot-path=/var/www/certbot
: Specifies webroot authentication method and path. --agree-tos --no-eff-email
: Agrees with the terms of service and disables the EFF email subscription. -d despam.io
: Specifies the domain for which to obtain the certificate.
Now, let's modify the nginx/nginx.conf file to accommodate for certbot challenge.
worker_processes 4;
events { worker_connections 1024; }
http {
sendfile on;
server_tokens off;
server {
listen 80;
server_name despam.io;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
proxy_pass http://web:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
worker_processes
: This directive sets the number of worker processes that Nginx should use. In this case, you've specified 4 worker processes. Nginx uses multiple worker processes to handle incoming client requests efficiently. The worker_connections
directive sets the maximum number of connections each worker process can handle.
server
: This block defines the configuration for a specific server block. In this case, it's the configuration for the HTTP server on port 80. listen 80; listen [::]:80;
: These directives specify that the server should listen on both IPv4 and IPv6 addresses on port 80. location /.well-known/acme-challenge/ { ... }
: This location block handles requests to the .well-known/acme-challenge/
path, which is used by Certbot for domain validation during certificate issuance. It specifies that the files for validation are located in the /var/www/certbot
directory.
location / { ... }
: This location block handles all other requests and acts as a reverse proxy to your FastAPI application running on port 8000. It passes the requests to the web
service defined in your Docker Compose configuration. The proxy_set_header
directives are used to forward necessary headers to the FastAPI application. Now, we can try to up our services. If you face a permission denied error the you will have to change the necessary permissions of filesystem as below.
despam@mlapi:~/mlapi-spam-prediction$ docker-compose -f docker-compose-prod.yml up --build
[+] Building 0.3s (5/9)
------
failed to solve: error from sender: open /home/despam/mlapi-spam-prediction/nginx/certbot/conf/accounts: permission denied
despam@mlapi:~/mlapi-spam-prediction$ sudo chown -R despam:despam /home/despam/mlapi-spam-prediction/nginx/certbot
[sudo] password for despam:
Now, We can definitely up the services, additionally, look at the logs very carefully. Logs will inform if we have successfully received the certificates.
despam@mlapi:~/mlapi-spam-prediction$ docker-compose -f docker-compose-prod.yml up --build
[+] Building 0.3s (10/10) FINISHED
mlapi-spam-prediction-certbot-1 | Account registered.
mlapi-spam-prediction-certbot-1 | Requesting a certificate for despam.io
# Below are hits for the well-known challenge.
mlapi-spam-prediction-nginx-1 | 23.178.112.209 - - [13/Sep/2023:04:58:57 +0000] "GET /.well-known/acme-challenge/K2FSI9az7Zdbi6F0I2WysLVHrqzfcugCJWFwMBPOh-Q HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"
mlapi-spam-prediction-web-1 | INFO: Started server process [7]
mlapi-spam-prediction-web-1 | INFO: Waiting for application startup.
mlapi-spam-prediction-web-1 | INFO: Application startup complete.
mlapi-spam-prediction-certbot-1 |
mlapi-spam-prediction-certbot-1 | Successfully received certificate.
mlapi-spam-prediction-certbot-1 | Certificate is saved at: /etc/letsencrypt/live/despam.io/fullchain.pem
mlapi-spam-prediction-certbot-1 | Key is saved at: /etc/letsencrypt/live/despam.io/privkey.pem
Once everything works correctly we can manually check if the certificate is synced to our local VM directory. We can also open the file and check the contents using the below code.
despam@mlapi:~/codeshare$ cd nginx/certbot/conf/
despam@mlapi:~/codeshare/nginx/certbot/conf$ ls
accounts archive live renewal renewal-hooks
#to see private key
despam@mlapi:~/codeshare/nginx/certbot/conf$ sudo cat ./live/despam.io/privkey.pem
Warning: if we repeatedly keep starting and shutting down the certbot service then we can hit the max rate-limit of certificate generation. So, once the certificate is generated switch to renew command in the docker-compose-prod.yml file.
certbot:
image: certbot/certbot:latest
command: renew #use renew command after your ssl is configured correctly and you can visit on HTTPS
volumes:
- ./nginx/certbot/www/:/var/www/certbot/:rw
- ./nginx/certbot/conf/:/etc/letsencrypt/:rw
Also, now we can start redirecting requests to https version with the ssl key. Modify the nginx.conf as below:
worker_processes 4;
events {
worker_connections 1024;
}
http {
sendfile on;
server_tokens off;
server {
listen 80;
server_name despam.io;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name despam.io;
ssl_certificate /etc/nginx/ssl/live/despam.io/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/live/despam.io/privkey.pem;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
proxy_pass http://web:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}
Finally, we can do a docker-compose -f docker-compose-prod.yml up --build -d to start the server in the background.
Challenge: Set up a cronjob that renews the certificate every 3 months.
Challenge Solution: Please try yourself first.
despam@mlapi:~/codeshare$ crontab -e
#add this line
0 0 1 */2 * docker-compose -f /home/full_path_to_prod_.yml run certbot renew
Brige the gap between Tutorial hell and Industry. We want to bring in the culture of Clean Code, Test Driven Development.
We know, we might make it hard for you but definitely worth the efforts.
© Copyright 2022-23 Team FastAPITutorial