Bare metal

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.ie that 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.

VariableDefaultPurpose
EAG_PORT8080Port to listen on
EAG_DB_DRIVERsqliteStorage engine: sqlite or postgres
EAG_DB_PATHeagraiclainne.dbSQLite database file (sqlite driver only)
EAG_DB_HOSTlocalhostPostgreSQL host (postgres driver only, like the rest of the EAG_DB_* connection settings)
EAG_DB_PORT5432PostgreSQL port
EAG_DB_USERpostgresDatabase user
EAG_DB_PASSWORD(empty)Database password
EAG_DB_NAMEeagraiclainneDatabase name
EAG_DB_SSLMODEdisableSSL 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_EXPIRY12hSign-in token lifetime
EAG_INITIAL_ADMIN_TOKEN(empty)Optional bootstrap token for automated first setup
EAG_READ_TIMEOUT1sHTTP read timeout
EAG_WRITE_TIMEOUT10sHTTP write timeout
EAG_OTLP_ENDPOINT(empty)OpenTelemetry collector host:port. Empty logs to stdout
EAG_OTLP_PROTOCOLgrpcOTLP transport (grpc or http)
EAG_OTLP_INSECUREfalseDisable 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:8080first sign-in takes it from there.