I would like to deploy a django application with the django-tenants library.
The project works fine locally, I can create tenants and access them via localhost:8000
and <tenant>.localhost:8000
(mind that this redirects to localhost:8000/en
and <tenant>.localhost:8000/en
because of internationalization)
Now I am trying to deploy the application on DigitalOcean using docker-compose and I have an issue with configuring the nginx server.
As the log suggests I am able to start the gunicorn server on 0.0.0.0:8000
but I can not reach the application through a client browser:
http://<DOMAIN>
returns 504 - Gateway Time-out
Here is my NGINX config (at /etc/nginx/sites-enabled):
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name .<DOMAIN>;
set $base /etc/nginx/sites-available/trackeree.com;
# SSL
ssl_certificate /etc/letsencrypt/live/trackeree.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/trackeree.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/trackeree.com/chain.pem;
# security
include /etc/nginx/security.conf;
# logging
access_log /var/log/nginx/access.log combined buffer=512k flush=1m;
error_log /var/log/nginx/error.log warn;
location / {
uwsgi_pass web:8000;
include /etc/nginx/uwsgi.conf;
}
# Django media
location /media/ {
alias $base/media/;
}
# Django static
location /static/ {
alias $base/static/;
}
# additional config
include /etc/nginx/general.conf;
}
# non-www, subdomains redirect
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name www.<DOMAIN>;
# SSL
ssl_certificate /etc/letsencrypt/live/trackeree.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/trackeree.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/trackeree.com/chain.pem;
return 301 https://trackeree.com$request_uri;
}
# HTTP redirect
server {
listen 80;
listen [::]:80;
server_name .<DOMAIN>;
# ACME-challenge
location ^~ /.well-known/acme-challenge/ {
root /etc/letsencrypt/www/;
}
location / {
return 301 https://trackeree.com$request_uri;
}
uwsgi.conf
# default uwsgi_params
include /etc/nginx/uwsgi_params;
# uwsgi settings
uwsgi_param Host $host;
uwsgi_param X-Real-IP $remote_addr;
uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for;
uwsgi_param X-Forwarded-Proto $http_x_forwarded_proto;
uwsgi_params
uwsgi_param REQUEST_METHOD $request_method;
uwsgi_param CONTENT_TYPE $content_type;
uwsgi_param CONTENT_LENGTH $content_length;
uwsgi_param REQUEST_URI $request_uri;
uwsgi_param PATH_INFO $document_uri;
uwsgi_param DOCUMENT_ROOT $document_root;
uwsgi_param SERVER_PROTOCOL $server_protocol;
uwsgi_param REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param SERVER_ADDR $server_addr;
uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_NAME $server_name;
When accessing https://<DOMAIN>
in the browser:
I can not see any error message in gunicorn logs:
[2023-08-20 23:28:45 +0000] [9] [INFO] Listening at: http://0.0.0.0:8000 (9)
[2023-08-20 23:28:45 +0000] [9] [INFO] Using worker: sync
[2023-08-20 23:28:45 +0000] [10] [INFO] Booting worker with pid: 10
[2023-08-20 23:28:45 +0000] [11] [INFO] Booting worker with pid: 11
And this is all I can see in the nginx logs:
2023/08/21 18:33:22 [error] 15#15: *1 upstream timed out (110: Operation timed out) while reading response header from upstream, client: <CLIENT_IP>, server: <DOMAIN>, request: "GET / HTTP/2.0", upstream: "uwsgi://<HOST_IP>:8000", host: "<DOMAIN>"
<CLIENT_IP> - - [21/Aug/2023:18:33:22 +0000] "GET / HTTP/2.0" 504 562 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36"
<CLIENT_IP> - - [21/Aug/2023:18:33:25 +0000] "GET / HTTP/2.0" 499 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36"
2023/08/21 18:34:25 [error] 15#15: *1 upstream timed out (110: Operation timed out) while reading response header from upstream, client: <CLIENT_IP>, server: <DOMAIN>, request: "GET / HTTP/2.0", upstream: "uwsgi://<HOST_IP>:8000", host: "<DOMAIN>"
Even if I put this in the nginx config the http response is still 504
location / {
uwsgi_pass <DOMAIN>:8000;
include /etc/nginx/uwsgi.conf;
}
From the proxy/nginx container:
bash-5.1# curl -vf web:8000
* Trying 172.29.0.7:8000...
* Connected to web (172.29.0.7) port 8000 (#0)
> GET / HTTP/1.1
> Host: web:8000
> User-Agent: curl/7.83.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 301 Moved Permanently
< Server: gunicorn
< Date: Mon, 21 Aug 2023 18:42:51 GMT
< Connection: close
< Transfer-Encoding: chunked
< Content-Type: text/html; charset=utf-8
< Location: https://web:8000/
< X-Content-Type-Options: nosniff
< Referrer-Policy: same-origin
< Cross-Origin-Opener-Policy: same-origin
<
* Closing connection 0
bash-5.1# curl -vf https://web:8000/en
* Trying 172.29.0.7:8000...
* Connected to web (172.29.0.7) port 8000 (#0)
* ALPN: offers h2
* ALPN: offers http/1.1
* CAfile: /etc/ssl/certs/ca-certificates.crt
* CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
The public tenant is successfully created with domain=<DOMAIN>
and I tried to create the public tenant to match the container name as well: domain=web
I can access the postgresql database from the web
container
All services are on the same docker network:
[
{
"Name": "saas-boilerplate_default",
"Id": "5c27de32563c949bc7b1848ad3fc76fa434b53aa1ddf9bf032c4a5e5a79ad226",
"Created": "2023-08-20T22:43:58.160670257Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.29.0.0/16",
"Gateway": "172.29.0.1"
}
]
},
"Internal": false,
"Attachable": true,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"02d013201492657e2be4614ead90f63c8356f89b6bb5e932a76f2d07ea616aa2": {
"Name": "saas-boilerplate_redis_1",
"EndpointID": "7cb890f1aeb79405efbc8093e583e84618a93742dcd52b6b3b8e7b3da48dbaba",
"MacAddress": "02:42:ac:1d:00:06",
"IPv4Address": "172.29.0.6/16",
"IPv6Address": ""
},
"15c86cdd4dd14b1af29dd9d60b84fabf0dd49dd5af22f050cde1165e1e652d29": {
"Name": "saas-boilerplate_flower_1",
"EndpointID": "bd2c4a86bd28e93c592276ffab4f7fa773575a4673bc442b759bdb67902b3f52",
"MacAddress": "02:42:ac:1d:00:02",
"IPv4Address": "172.29.0.2/16",
"IPv6Address": ""
},
"50d275ae08b4591621a467e7559db52857f60dcdfd5bef8adffe941e4fd5f093": {
"Name": "saas-boilerplate_db_1",
"EndpointID": "8f250e357ed3cef570e77a5359f6e7bbe4aab951ceb28c5d64921547da5094de",
"MacAddress": "02:42:ac:1d:00:03",
"IPv4Address": "172.29.0.3/16",
"IPv6Address": ""
},
"5608aaefa2eafd7d0f6bd213865169dcb910911989df4f3f35f7ef3cb63aac4c": {
"Name": "saas-boilerplate_celery_worker_1",
"EndpointID": "e13441d0ffbebf77516204e76292186caa706eb3678f486e57756d0483762712",
"MacAddress": "02:42:ac:1d:00:04",
"IPv4Address": "172.29.0.4/16",
"IPv6Address": ""
},
"e10302100dcb45f4c80686ca7cde6be0d6b698bece4fcca5974d665d38d80352": {
"Name": "saas-boilerplate_web_1",
"EndpointID": "a37d53b8e5137c08c2c3821ced92ee3e3fb3ae2739293a4df23e343f16d44b89",
"MacAddress": "02:42:ac:1d:00:07",
"IPv4Address": "172.29.0.7/16",
"IPv6Address": ""
},
"e8b1564ec18bb763e8f7e0734ae2e4f0249148f1065d071eb3d71da8b14ad0a3": {
"Name": "saas-boilerplate_celery_beat_1",
"EndpointID": "56d3078bea5ad480b293c59bf6a4fab0dbc67a4e0923479e5a648681b3c4fac1",
"MacAddress": "02:42:ac:1d:00:05",
"IPv4Address": "172.29.0.5/16",
"IPv6Address": ""
},
"eb3da21f295108aad66e247f56a2255e3c3bdab5bf0e09494c6bc8c8ddcbcfd5": {
"Name": "saas-boilerplate_proxy_1",
"EndpointID": "155b7121435ed1d948e0ce543571a668e0eb45165654db151d3e99d7d1b851fb",
"MacAddress": "02:42:ac:1d:00:08",
"IPv4Address": "172.29.0.8/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {
"com.docker.compose.network": "default",
"com.docker.compose.project": "saas-boilerplate",
"com.docker.compose.version": "1.29.2"
}
}
]
I start gunicorn with this start script:
#!/bin/bash
set -o errexit set -o nounset
python manage.py migrate gunicorn --bind 0.0.0.0:8000 --timeout 600
--workers 2 trackr.wsgi:application
I can successfully get SSL certificate and that is why I think that my nginx configuration is correct, but somehow the application is still unreachable.
I masked the DOMAIN, CLIENT_IP and HOST_IP for security reasons.
I really don't know where to go from here. What am I missing?