better readnme for gunicorn deployment

This commit is contained in:
2026-07-25 22:01:34 +02:00
parent 1e87b5f385
commit 61377c7c77
4 changed files with 220 additions and 2 deletions
Vendored
BIN
View File
Binary file not shown.
+218 -1
View File
@@ -44,7 +44,224 @@ flask:
Set `URL_PREFIX` in the environment to override this value at deployment time.
Use an empty value (`url_prefix: ""` or `URL_PREFIX=`) to serve from `/` instead.
The reverse proxy must preserve the prefix when forwarding requests to Waitress.
The reverse proxy must preserve the prefix when forwarding requests to the WSGI
server (Waitress or Gunicorn).
## Production deployment: Nginx → Gunicorn → Fatima
The following example targets a Debian/Ubuntu server using:
- `/srv/fatima` for the application and virtual environment
- `/etc/fatima/config.yaml` for secrets and application configuration
- `/var/lib/fatima/calendar.sqlite` for persistent data
- `/run/fatima/gunicorn.sock` for the private Gunicorn socket
- `fatima.service` to run the application as an unprivileged `fatima` user
Replace `calendar.example.com` and the sample credentials before exposing the
site. The examples assume the application remains mounted at `/fatima`.
### 1. Install the application
Install the operating-system packages and create a service account:
```sh
sudo apt update
sudo apt install nginx python3 python3-venv
sudo useradd --system --user-group --home-dir /srv/fatima --shell /usr/sbin/nologin fatima
```
Copy or clone the repository into `/srv/fatima`, then create the virtual
environment and install the dependencies. Gunicorn is an additional production
dependency; Waitress remains suitable for the local command documented above.
```sh
sudo chown -R fatima:fatima /srv/fatima
sudo -u fatima python3 -m venv /srv/fatima/.venv
sudo -u fatima /srv/fatima/.venv/bin/pip install -r /srv/fatima/requirements.txt gunicorn
```
For repeatable deployments, pin the Gunicorn version used by the server in your
deployment tooling or requirements file.
### 2. Create the production configuration
Generate a signing key:
```sh
python3 -c "import secrets; print(secrets.token_hex(32))"
```
Create `/etc/fatima/config.yaml` using `config.example.yaml` as a guide:
```sh
sudo install -d -o root -g fatima -m 750 /etc/fatima
```
```yaml
flask:
secret_key: "paste-the-generated-random-value-here"
url_prefix: "/fatima"
log_file: "/var/log/fatima/startup.log"
database: "/var/lib/fatima/calendar.sqlite"
auth:
username: "replace-with-the-login-name"
password: "replace-with-a-strong-password"
amounts:
presets:
- "55"
- "27.50"
```
Protect the file because it contains the login password and Flask signing key:
```sh
sudo chown root:fatima /etc/fatima/config.yaml
sudo chmod 640 /etc/fatima/config.yaml
```
The systemd unit below creates `/var/lib/fatima`, `/var/log/fatima`, and
`/run/fatima` with ownership suitable for the service. If an existing database
is copied into `/var/lib/fatima`, make sure it and its parent directory are
writable by `fatima`; SQLite may create journal files beside the database.
### 3. Run Gunicorn with systemd
Create `/etc/systemd/system/fatima.service`:
```ini
[Unit]
Description=Fatima calendar
After=network.target
[Service]
Type=simple
User=fatima
Group=www-data
SupplementaryGroups=fatima
WorkingDirectory=/srv/fatima
Environment=APP_CONFIG=/etc/fatima/config.yaml
ExecStart=/srv/fatima/.venv/bin/gunicorn \
--workers 2 \
--bind unix:/run/fatima/gunicorn.sock \
--umask 007 \
--access-logfile - \
--error-logfile - \
flask_fatima:app
Restart=on-failure
RestartSec=5
RuntimeDirectory=fatima
RuntimeDirectoryMode=0750
StateDirectory=fatima
StateDirectoryMode=0750
LogsDirectory=fatima
LogsDirectoryMode=0750
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
[Install]
WantedBy=multi-user.target
```
`Group=www-data` and `--umask 007` allow Nginx to connect to the Unix socket
without exposing it to other users. `APP_CONFIG` makes the application load the
production YAML file rather than `/srv/fatima/config.yaml`.
Load and start the service:
```sh
sudo systemctl daemon-reload
sudo systemctl enable --now fatima
sudo systemctl status fatima
sudo journalctl -u fatima -n 50 --no-pager
```
The startup log should show the resolved production database path. Before
configuring Nginx, verify Gunicorn directly:
```sh
sudo -u www-data curl --unix-socket /run/fatima/gunicorn.sock \
--head http://localhost/fatima/
```
A redirect to `/fatima/login` is the expected response when no authenticated
session exists.
### 4. Proxy `/fatima` through Nginx
Create `/etc/nginx/sites-available/fatima`:
```nginx
upstream fatima_gunicorn {
server unix:/run/fatima/gunicorn.sock;
}
server {
listen 80;
listen [::]:80;
server_name calendar.example.com;
location = /fatima {
return 301 /fatima/;
}
location /fatima/ {
proxy_pass http://fatima_gunicorn;
proxy_http_version 1.1;
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;
}
}
```
There is deliberately no URI after `proxy_pass`: Nginx must forward
`/fatima/...` unchanged because Fatima's prefix middleware removes that prefix
before Flask routes the request. Do not rewrite it to `/`.
Enable and validate the site:
```sh
sudo ln -s /etc/nginx/sites-available/fatima /etc/nginx/sites-enabled/fatima
sudo nginx -t
sudo systemctl reload nginx
curl --head http://calendar.example.com/fatima/
```
Add TLS before exposing the login publicly. Once the certificate is configured,
redirect HTTP to HTTPS and keep the same `/fatima/` proxy block in the TLS
server. The application stores authentication in a signed browser session, so
keep `secret_key` stable across restarts and deployments; changing it logs out
all existing sessions.
### Updating and troubleshooting
After deploying new source or dependencies:
```sh
sudo -u fatima /srv/fatima/.venv/bin/pip install -r /srv/fatima/requirements.txt
sudo systemctl restart fatima
sudo systemctl reload nginx
```
Useful checks:
```sh
sudo journalctl -u fatima -f
sudo tail -f /var/log/nginx/error.log
sudo nginx -t
sudo systemctl status fatima nginx
```
If Nginx returns `502 Bad Gateway`, confirm that `fatima.service` is running and
that `www-data` can access `/run/fatima/gunicorn.sock`. If the app starts but
cannot save calendar entries, check ownership of `/var/lib/fatima` and confirm
the startup summary names `/var/lib/fatima/calendar.sqlite`.
## Import the Excel calendar
+2 -1
View File
@@ -1,4 +1,5 @@
Flask==3.1.1
PyYAML==6.0.2
openpyxl==3.1.5
waitress==3.0.2
gunicorn
Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB