No containers anywhere: build the binary from source and run it under
systemd. Storage defaults to SQLite, so the whole install is the binary
plus one database file under /var/lib/eagraiclainne — no
database server unless you opt into PostgreSQL.
Prerequisites
- Go 1.25+, Node 22+, and buf (generates the TypeScript protobuf clients the web build needs)
- A Forgejo account on
forgejo.lihnet.mallon.iethat can clone the repository
Build from source
The release tarballs carry the CLI only. You build the server from a
clone. make build-all builds the web UI first, then embeds it
in the server binary:
git clone https://forgejo.lihnet.mallon.ie/eagraiclainne/app.git
cd eagraiclainne
git checkout v1.0.0
make web-install # npm install for the web build
make web-gen # buf generate — TypeScript clients (not committed)
make build-all # web UI, then the server with it embedded
sudo install bin/server /usr/local/bin/eagraiclainne-server
The systemd service
Configuration is environment variables with the EAG_
prefix. The database file goes under /var/lib/eagraiclainne,
which StateDirectory= creates and owns for the service.
There is no schema step — the binary embeds the schema and applies it
idempotently on every boot. Keep the settings in a root-owned file:
sudo install -d -m 750 /etc/eagraiclainne
sudo tee /etc/eagraiclainne/env >/dev/null <<'EOF'
EAG_DB_PATH=/var/lib/eagraiclainne/eagraiclainne.db
EOF
sudo chmod 640 /etc/eagraiclainne/env
sudo useradd --system --no-create-home --shell /usr/sbin/nologin eagraiclainne
sudo chgrp eagraiclainne /etc/eagraiclainne /etc/eagraiclainne/env
sudo tee /etc/systemd/system/eagraiclainne.service >/dev/null <<'EOF'
[Unit]
Description=eagraiclainne family noticeboard
After=network-online.target
Wants=network-online.target
[Service]
User=eagraiclainne
Group=eagraiclainne
EnvironmentFile=/etc/eagraiclainne/env
ExecStart=/usr/local/bin/eagraiclainne-server
StateDirectory=eagraiclainne
Restart=on-failure
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now eagraiclainne
PostgreSQL (optional)
If you prefer a database server, install PostgreSQL 15 or newer (the
project tests against 17), create a role and database, and swap the env
file's storage lines. Add postgresql.service to the unit's
After= so boot ordering holds. The schema still applies
itself on boot, under an advisory lock:
sudo -u postgres psql <<'EOF'
CREATE ROLE eagraiclainne LOGIN PASSWORD 'change-me';
CREATE DATABASE eagraiclainne OWNER eagraiclainne;
EOF
# /etc/eagraiclainne/env — postgres instead of the sqlite default
EAG_DB_DRIVER=postgres
EAG_DB_HOST=localhost
EAG_DB_PORT=5432
EAG_DB_USER=eagraiclainne
EAG_DB_PASSWORD=change-me
EAG_DB_NAME=eagraiclainne
EAG_DB_SSLMODE=disable
Environment reference
Every flag of cmd/server is readable from the environment:
prefix EAG_, dashes become underscores.
| Variable | Default | Purpose |
|---|---|---|
EAG_PORT | 8080 | Port to listen on |
EAG_DB_DRIVER | sqlite | Storage engine: sqlite or postgres |
EAG_DB_PATH | eagraiclainne.db | SQLite database file (sqlite driver only) |
EAG_DB_HOST | localhost | PostgreSQL host (postgres driver only, like the rest of the EAG_DB_* connection settings) |
EAG_DB_PORT | 5432 | PostgreSQL port |
EAG_DB_USER | postgres | Database user |
EAG_DB_PASSWORD | (empty) | Database password |
EAG_DB_NAME | eagraiclainne | Database name |
EAG_DB_SSLMODE | disable | SSL mode for the connection |
EAG_UPDATE_FEED | (empty) | Release feed the admin's on-demand update check asks. Empty disables the check. Never queried in the background |
EAG_JWT_SECRET | (empty) | Token signing secret. Empty means the server generates one on first boot and stores it in the database. Set it (32+ bytes) only to pin the secret externally |
EAG_TOKEN_EXPIRY | 12h | Sign-in token lifetime |
EAG_INITIAL_ADMIN_TOKEN | (empty) | Optional bootstrap token for automated first setup |
EAG_READ_TIMEOUT | 1s | HTTP read timeout |
EAG_WRITE_TIMEOUT | 10s | HTTP write timeout |
EAG_OTLP_ENDPOINT | (empty) | OpenTelemetry collector host:port. Empty logs to stdout |
EAG_OTLP_PROTOCOL | grpc | OTLP transport (grpc or http) |
EAG_OTLP_INSECURE | false | Disable TLS for the OTLP exporter |
Verify
systemctl status eagraiclainne
curl -sS http://localhost:8080/ | head -c 200
journalctl -u eagraiclainne -f # watch the boot: schema apply, then serving
Then open http://your-server:8080 —
first sign-in takes it from
there.