Added a systemd unit and logrotate configuration, packaged in the release

This commit is contained in:
bwilliams committed 2026-10-21 15:31:35 +01:00
1 parent 1e3acaeff3
commit 2aeb2993b0
4 files changed
+137 -2

No files matched your search

+32
View File
@@ -39,6 +39,37 @@ The `sqlite_fts5` tag is needed for the database's title search (see
[Testing](#testing)). [Testing](#testing)).
## Running as a service
`deploy/job-search.service` is a systemd unit running `job-search serve`
(the server and every enabled source's fetcher) as a `job-search` system
user, from `/usr/local/bin/job-search` with `/etc/job-search/config.yaml`.
Its comments give the install steps. It keeps its data in
`/var/lib/job-search` and its log in `/var/log/job-search`, the only places
it can write, so set `database.file` and `logging.file` there.
- The database isn't migrated on start: run `job-search migrate up`
(`--create` the first time) as the `job-search` user before the first
start and after each upgrade.
- A failing source stops `serve` on purpose; the unit restarts it after
30s.
### Rotating the logs
`job-search` opens its log file once and has no signal to reopen it, so
logrotate must copy the file and truncate it in place (`copytruncate`); a
line written between the two is lost. `deploy/job-search.logrotate` does
this weekly for every `*.log` in `/var/log/job-search` (the log and, if
`server.access_log` is set there, the access log), keeping 8 weeks,
compressed. `/var/log/job-search` belongs to the service user, so it rotates
as that user (`su job-search job-search`); logrotate refuses otherwise.
```bash
# from the release tarball's directory (deploy/job-search.logrotate in the repo)
sudo install -m 644 job-search.logrotate /etc/logrotate.d/job-search
sudo logrotate --debug /etc/logrotate.d/job-search # dry run: what it would do
sudo logrotate --force /etc/logrotate.d/job-search # rotate now, to check
```
## Configuration ## Configuration
Configuration is read from, in increasing priority: Configuration is read from, in increasing priority:
1. Built-in defaults 1. Built-in defaults
@@ -212,6 +243,7 @@ second one fails with "address already in use".
| `internal/version` | Version information set at build time | | `internal/version` | Version information set at build time |
| `internal/frontend` | Serves the frontend's build output; embeds `dist/`, copied there from `web/dist` by `npm run sync` | | `internal/frontend` | Serves the frontend's build output; embeds `dist/`, copied there from `web/dist` by `npm run sync` |
| `web` | The frontend (see [web](web/README.md)) | | `web` | The frontend (see [web](web/README.md)) |
| `deploy` | The systemd unit and logrotate configuration (see [Running as a service](#running-as-a-service)) |
## Testing ## Testing
+18
View File
@@ -0,0 +1,18 @@
# logrotate configuration for job-search's logs (see README.md, "Rotating
# the logs"). Install as /etc/logrotate.d/job-search; it is in the release
# tarball next to the binary.
#
# job-search opens its log file once and has no signal to reopen it, so the
# file is copied and then truncated in place (copytruncate). A line written
# between the copy and the truncation is lost.
/var/log/job-search/*.log {
weekly
rotate 8
compress
delaycompress
missingok
notifempty
copytruncate
# /var/log/job-search belongs to the service user, so rotate as it.
su job-search job-search
}
+80
View File
@@ -0,0 +1,80 @@
# job-search: serves the API and frontend, and runs the enabled sources'
# fetchers (job-search serve). See README.md, "Running as a service".
#
# Install, from the release tarball's directory (job-search-<version>-<arch>):
# sudo useradd --system --home-dir /var/lib/job-search --shell /usr/sbin/nologin job-search
# sudo install -m 755 job-search /usr/local/bin/job-search
# sudo install -d -m 750 -o root -g job-search /etc/job-search
# sudo install -m 640 -o root -g job-search config.yaml /etc/job-search/config.yaml
# sudo install -d -m 750 -o job-search -g job-search /var/lib/job-search /var/log/job-search
# sudo -u job-search /usr/local/bin/job-search --config /etc/job-search/config.yaml migrate up --create
# sudo install -m 644 job-search.service /etc/systemd/system/
# sudo systemctl daemon-reload
# sudo systemctl enable --now job-search
#
# /etc/job-search/config.yaml should set at least:
# database:
# file: /var/lib/job-search/jobsearch.db
# logging:
# file: /var/log/job-search/job-search.log
# Without logging.file, logs go to the journal (as coloured text). Rotate the
# log file with job-search.logrotate:
# sudo install -m 644 job-search.logrotate /etc/logrotate.d/job-search
#
# After each upgrade, migrate the database before restarting:
# sudo -u job-search /usr/local/bin/job-search --config /etc/job-search/config.yaml migrate up
# sudo systemctl restart job-search
[Unit]
Description=Job Search: procurement opportunities server and fetcher
Documentation=https://git.avsdev.uk/Go/job-search
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=job-search
Group=job-search
# /var/lib/job-search and /var/log/job-search, created and owned by the
# service user; the only places it can write.
StateDirectory=job-search
StateDirectoryMode=0750
LogsDirectory=job-search
LogsDirectoryMode=0750
WorkingDirectory=/var/lib/job-search
ExecStart=/usr/local/bin/job-search --config /etc/job-search/config.yaml serve
# serve shuts down cleanly on SIGTERM: the HTTP server drains and in-flight
# fetches get up to 10s.
KillSignal=SIGTERM
TimeoutStopSec=30
# A failing source stops serve on purpose; restart to retry it.
Restart=on-failure
RestartSec=30
UMask=0027
# Hardening
NoNewPrivileges=yes
CapabilityBoundingSet=
AmbientCapabilities=
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
ProtectControlGroups=yes
ProtectClock=yes
ProtectHostname=yes
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
LockPersonality=yes
MemoryDenyWriteExecute=yes
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @resources
[Install]
WantedBy=multi-user.target
+7 -2
View File
@@ -62,12 +62,17 @@ go build \
echo "Built $BUILD_DIR/job-search-$VERSION" echo "Built $BUILD_DIR/job-search-$VERSION"
# If proper release, copy to output directory for packaging # If proper release, copy to output directory for packaging, with the
# systemd unit and logrotate configuration (see README.md, "Running as a
# service")
if [[ $DO_RELEASE -eq 1 ]]; then if [[ $DO_RELEASE -eq 1 ]]; then
install -DT $BUILD_DIR/job-search-$VERSION $RELEASE_DIR/job-search install -DT $BUILD_DIR/job-search-$VERSION $RELEASE_DIR/job-search
install -DT -m 644 deploy/job-search.service $RELEASE_DIR/job-search.service
install -DT -m 644 deploy/job-search.logrotate $RELEASE_DIR/job-search.logrotate
ARCH=`dpkg --print-architecture` ARCH=`dpkg --print-architecture`
tar -czf $RELEASE_DIR/job-search-$VERSION-$ARCH.tar.gz \ tar -czf $RELEASE_DIR/job-search-$VERSION-$ARCH.tar.gz \
--transform "s,^$RELEASE_DIR/,job-search-$VERSION-$ARCH/," \ --transform "s,^$RELEASE_DIR/,job-search-$VERSION-$ARCH/," \
$RELEASE_DIR/job-search $RELEASE_DIR/job-search $RELEASE_DIR/job-search.service \
$RELEASE_DIR/job-search.logrotate
echo "Installed $RELEASE_DIR/job-search" echo "Installed $RELEASE_DIR/job-search"
fi fi