from there.
Rather than the standalone mode suggested on the web page, I decided to set
-it up in what felt like a more robust way using WSGI. I installed `uwsgi`,
-`uwsgi-plugin-python3`, and `libapache2-mod-proxy-uwsgi`, and created the
-following file in `/etc/uwsgi/apps-available/xandikos.ini` which I then
-symlinked into `/etc/uwsgi/apps-enabled/xandikos.ini`:
+it up in what felt like a more robust way using WSGI. I installed
+`gunicorn` and `python3-gunicorn`, created the following file in
+`/etc/systemd/system/xandikos.socket`:
:::ini
- [uwsgi]
- socket = 127.0.0.1:8801
- uid = xandikos
- gid = xandikos
- umask = 022
- master = true
- cheaper = 2
- processes = 4
- plugin = python3
- module = xandikos.wsgi:app
- env = XANDIKOSPATH=/srv/xandikos/collections
-
-The port number was arbitrary, as was the path. You need to create the
-`xandikos` user and group first (`adduser --system --group --no-create-home
---disabled-login xandikos`). I created `/srv/xandikos` owned by
-`xandikos:xandikos` and mode 0700, and I recommend [setting a
-umask](https://bugs.debian.org/866058) as shown above since uwsgi's default
-umask is 000 (!). You should also run `sudo -u xandikos xandikos -d
-/srv/xandikos/collections --autocreate` and then Ctrl-c it after a short
-time (I think it would be nicer if there were a way to [ask the WSGI wrapper
-to do this](https://bugs.debian.org/866093)).
-
-For Apache setup, I kept it reasonably simple: I ran `a2enmod proxy_uwsgi`,
+ [Unit]
+ Description=Xandikos socket
+
+ [Socket]
+ ListenStream=/run/xandikos.socket
+
+ [Install]
+ WantedBy=sockets.target
+
+... and the following file in `/etc/systemd/system/xandikos.service`:
+
+ :::ini
+ [Unit]
+ Description=Xandikos CalDAV/CardDAV server
+ Documentation=man:xandikos(1)
+ Requires=xandikos.socket
+
+ [Service]
+ User=xandikos
+ Group=xandikos
+ Restart=on-failure
+ ExecStart=/usr/bin/python3 /usr/bin/gunicorn --bind=unix:/run/xandikos.socket xandikos.wsgi:app
+ ExecReload=/bin/kill -s HUP $MAINPID
+ ExecStop=/bin/kill -s TERM $MAINPID
+ Environment=XANDIKOSPATH=/srv/xandikos/collections
+ ProtectSystem=strict
+ ProtectKernelTunables=yes
+ ProtectControlGroups=yes
+ PrivateDevices=yes
+ PrivateTmp=yes
+ ReadWritePaths=/run/xandikos.socket /srv/xandikos
+
+The path (`/srv/xandikos/collections`) was arbitrary. You need to create
+the `xandikos` user and group first (`adduser --system --group
+--no-create-home --disabled-login xandikos`). I created `/srv/xandikos`
+owned by `xandikos:xandikos` and mode 0700. You should also run `sudo -u
+xandikos xandikos -d /srv/xandikos/collections --autocreate` and then Ctrl-c
+it after a short time (I think it would be nicer if there were a way to [ask
+the WSGI wrapper to do this](https://bugs.debian.org/866093)). If you
+aren't using systemd then you can of course write equivalent init scripts
+instead.
+
+For Apache setup, I kept it reasonably simple: I ran `a2enmod proxy_http`,
used `htpasswd` to create `/etc/apache2/xandikos.passwd` with a username and
password for myself, added a virtual host in
`/etc/apache2/sites-available/xandikos.conf`, and enabled it with `a2ensite
TransferLog /var/log/apache2/xandikos-access.log
<Location />
- ProxyPass "uwsgi://127.0.0.1:8801/"
+ ProxyPass "unix:/run/xandikos.socket|http://xandikos.riva.dynamic.greenend.org.uk/"
AuthType Basic
AuthName "Xandikos"
AuthBasicProvider file
</Location>
</VirtualHost>
+You should of course adjust the `ProxyPass` line to match your own
+deployment.
+
Then `service apache2 reload`, set the new virtual host up with [Let's
Encrypt](https://letsencrypt.org/), reloaded again, and off we go.