[Unit] Description=BookStack MCP Server Documentation=https://github.com/your/bookstack-mcp After=network-online.target Wants=network-online.target [Service] Type=exec User=bookstack-mcp Group=bookstack-mcp EnvironmentFile=/etc/bookstack-mcp/env WorkingDirectory=/opt/bookstack-mcp ExecStart=/opt/bookstack-mcp/venv/bin/python -m bookstack_mcp # ProtectSystem=strict makes everything read-only, but the MCP library writes # to a data directory under $HOME (OAuth client registrations, caches). The # service user's home is /opt/bookstack-mcp, which is part of the read-only # tree -- so point HOME and the XDG dirs at directories systemd creates and # grants write access to. Without this the service dies with # "OSError: [Errno 30] Read-only file system: '/opt/bookstack-mcp/.local'". StateDirectory=bookstack-mcp CacheDirectory=bookstack-mcp Environment=HOME=/var/lib/bookstack-mcp Environment=XDG_DATA_HOME=/var/lib/bookstack-mcp Environment=XDG_CONFIG_HOME=/var/lib/bookstack-mcp Environment=XDG_CACHE_HOME=/var/cache/bookstack-mcp Restart=on-failure RestartSec=5s TimeoutStopSec=20s # --- Hardening --- # These all work in an unprivileged Proxmox/LXD container. If the service fails # with a 226/NAMESPACE or 227/SECCOMP status on an older kernel, comment out the # block below and it will start fine -- it only reduces defence in depth. NoNewPrivileges=yes PrivateTmp=yes ProtectSystem=strict ProtectHome=yes ProtectControlGroups=yes RestrictSUIDSGID=yes RemoveIPC=yes LockPersonality=yes MemoryDenyWriteExecute=yes RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX CapabilityBoundingSet= AmbientCapabilities= # Journald captures stdout/stderr: journalctl -u bookstack-mcp -f StandardOutput=journal StandardError=journal SyslogIdentifier=bookstack-mcp [Install] WantedBy=multi-user.target