# /etc/systemd/system/cashumints-site.service # # Serves the built site on loopback. nginx proxies to it and never opens a file itself, # which is the point: when nginx held a `root` inside /home/cashumints, every directory # down to dist had to be traversable by www-data, and the one that was not took the # whole site down as a blanket 404 with nothing in the error log naming the cause. # # This is a long-running daemon, unlike cashumints-web.service next to it — that one is # the oneshot that produces what this one serves. [Unit] Description=cashumints.space static site server Wants=network-online.target After=network-online.target # Give up after five failures in a minute instead of restarting forever. A process that # cannot start will not start on the 4000th attempt either, and `failed` in # `systemctl status` is a far louder signal than a journal scrolling past. These two are # [Unit] keys; systemd ignores them under [Service] with only a warning. StartLimitIntervalSec=60 StartLimitBurst=5 # Not Requires=cashumints.service: the pages are prerendered, so the site keeps serving # a correct-as-of-last-build copy while the API is down. Only the islands go quiet. [Service] Type=simple User=cashumints Group=cashumints WorkingDirectory=/home/cashumints/CashuMints.space/web # The tree comes from cashumints-web.service, which rsyncs it here after a build. # Serving web/dist directly would mean a rebuild empties the site for the length of it. StateDirectory=cashumints Environment=NODE_ENV=production Environment=SITE_PORT=8789 Environment=SITE_HOST=127.0.0.1 Environment=WEB_ROOT=/var/lib/cashumints/web ExecStart=/usr/bin/node server.mjs Restart=on-failure RestartSec=5s KillSignal=SIGTERM # In-flight responses finish; idle keep-alive connections are closed at once. TimeoutStopSec=15s UMask=0027 NoNewPrivileges=true PrivateTmp=true PrivateDevices=true ProtectSystem=strict # Read-only rather than absent: server.mjs itself lives under /home/cashumints. ProtectHome=read-only ReadWritePaths=/var/lib/cashumints ProtectKernelTunables=true ProtectKernelModules=true ProtectControlGroups=true RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 RestrictSUIDSGID=true LockPersonality=true [Install] WantedBy=multi-user.target