updated for deployment on suy.nl

This commit is contained in:
2026-07-26 11:17:19 +02:00
parent 61377c7c77
commit 627795efce
2 changed files with 22 additions and 40 deletions
+21 -38
View File
@@ -37,7 +37,7 @@ The app is served below `/fatima` by default. Configure the external path in
```yaml ```yaml
flask: flask:
url_prefix: "/fatima" url_prefix: "" # no prefix if site sits under fatima.suy.nl
log_file: "/tmp/fatima.log" log_file: "/tmp/fatima.log"
database: "instance/calendar.sqlite" database: "instance/calendar.sqlite"
``` ```
@@ -51,8 +51,8 @@ server (Waitress or Gunicorn).
The following example targets a Debian/Ubuntu server using: The following example targets a Debian/Ubuntu server using:
- `/srv/fatima` for the application and virtual environment - `/srv/Fatima` for the application and virtual environment
- `/etc/fatima/config.yaml` for secrets and application configuration - `/etc/Fatima/config.yaml` for secrets and application configuration
- `/var/lib/fatima/calendar.sqlite` for persistent data - `/var/lib/fatima/calendar.sqlite` for persistent data
- `/run/fatima/gunicorn.sock` for the private Gunicorn socket - `/run/fatima/gunicorn.sock` for the private Gunicorn socket
- `fatima.service` to run the application as an unprivileged `fatima` user - `fatima.service` to run the application as an unprivileged `fatima` user
@@ -67,17 +67,17 @@ Install the operating-system packages and create a service account:
```sh ```sh
sudo apt update sudo apt update
sudo apt install nginx python3 python3-venv sudo apt install nginx python3 python3-venv
sudo useradd --system --user-group --home-dir /srv/fatima --shell /usr/sbin/nologin fatima 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 Copy or clone the repository into `/srv/Fatima`, then create the virtual
environment and install the dependencies. Gunicorn is an additional production environment and install the dependencies. Gunicorn is an additional production
dependency; Waitress remains suitable for the local command documented above. dependency; Waitress remains suitable for the local command documented above.
```sh ```sh
sudo chown -R fatima:fatima /srv/fatima sudo chown -R fatima:fatima /srv/Fatima
sudo -u fatima python3 -m venv /srv/fatima/.venv sudo -u fatima python3 -m venv /srv/Fatima/.venv
sudo -u fatima /srv/fatima/.venv/bin/pip install -r /srv/fatima/requirements.txt gunicorn 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 For repeatable deployments, pin the Gunicorn version used by the server in your
@@ -91,16 +91,11 @@ Generate a signing key:
python3 -c "import secrets; print(secrets.token_hex(32))" 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 ```yaml
flask: flask:
secret_key: "paste-the-generated-random-value-here" secret_key: "paste-the-generated-random-value-here"
url_prefix: "/fatima" url_prefix: ""
log_file: "/var/log/fatima/startup.log" log_file: "/var/log/fatima/startup.log"
database: "/var/lib/fatima/calendar.sqlite" database: "/var/lib/fatima/calendar.sqlite"
@@ -117,8 +112,8 @@ amounts:
Protect the file because it contains the login password and Flask signing key: Protect the file because it contains the login password and Flask signing key:
```sh ```sh
sudo chown root:fatima /etc/fatima/config.yaml sudo chown root:fatima /srv/Fatima/config.yaml
sudo chmod 640 /etc/fatima/config.yaml sudo chmod 640 /srv/Fatima/config.yaml
``` ```
The systemd unit below creates `/var/lib/fatima`, `/var/log/fatima`, and The systemd unit below creates `/var/lib/fatima`, `/var/log/fatima`, and
@@ -140,9 +135,8 @@ Type=simple
User=fatima User=fatima
Group=www-data Group=www-data
SupplementaryGroups=fatima SupplementaryGroups=fatima
WorkingDirectory=/srv/fatima WorkingDirectory=/srv/Fatima
Environment=APP_CONFIG=/etc/fatima/config.yaml ExecStart=/srv/Fatima/.venv/bin/gunicorn \
ExecStart=/srv/fatima/.venv/bin/gunicorn \
--workers 2 \ --workers 2 \
--bind unix:/run/fatima/gunicorn.sock \ --bind unix:/run/fatima/gunicorn.sock \
--umask 007 \ --umask 007 \
@@ -184,7 +178,7 @@ configuring Nginx, verify Gunicorn directly:
```sh ```sh
sudo -u www-data curl --unix-socket /run/fatima/gunicorn.sock \ sudo -u www-data curl --unix-socket /run/fatima/gunicorn.sock \
--head http://localhost/fatima/ --head http://localhost
``` ```
A redirect to `/fatima/login` is the expected response when no authenticated A redirect to `/fatima/login` is the expected response when no authenticated
@@ -204,38 +198,27 @@ server {
listen [::]:80; listen [::]:80;
server_name calendar.example.com; server_name calendar.example.com;
location = /fatima { location / {
return 301 /fatima/; proxy_pass http://unix:/run/fatima/gunicorn.sock;
}
location /fatima/ {
proxy_pass http://fatima_gunicorn;
proxy_http_version 1.1;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 60s;
} }
} }
``` ```
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 ```sh
sudo ln -s /etc/nginx/sites-available/fatima /etc/nginx/sites-enabled/fatima sudo ln -s /etc/nginx/sites-available/fatima /etc/nginx/sites-enabled/fatima
sudo nginx -t sudo nginx -t
sudo systemctl reload nginx sudo systemctl reload nginx
curl --head http://calendar.example.com/fatima/ curl --head http://calendar.example.com/
``` ```
Add TLS before exposing the login publicly. Once the certificate is configured, The application stores authentication in a signed browser session, so
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 keep `secret_key` stable across restarts and deployments; changing it logs out
all existing sessions. all existing sessions.
@@ -244,7 +227,7 @@ all existing sessions.
After deploying new source or dependencies: After deploying new source or dependencies:
```sh ```sh
sudo -u fatima /srv/fatima/.venv/bin/pip install -r /srv/fatima/requirements.txt sudo -u fatima /srv/Fatima/.venv/bin/pip install -r /srv/Fatima/requirements.txt
sudo systemctl restart fatima sudo systemctl restart fatima
sudo systemctl reload nginx sudo systemctl reload nginx
``` ```
+1 -2
View File
@@ -1,5 +1,4 @@
Flask==3.1.1 Flask==3.1.1
PyYAML==6.0.2 PyYAML==6.0.2
openpyxl==3.1.5 openpyxl==3.1.5
gunicorn gunicorn