From 2ebfa4caaffdc31af66120c644790213ed945044 Mon Sep 17 00:00:00 2001 From: Senad Date: Tue, 14 Jul 2026 12:26:01 +0200 Subject: [PATCH] Add container deployment: Dockerfile, compose, systemd unit, redeploy script Multi-stage build (node -> go -> alpine) producing the static tefterd binary with the frontend embedded. Compose follows the house style for the podman/SELinux server (:Z volume label + label:disable). deploy/ holds the user systemd unit and redeploy-tefter.sh, mirroring the apelacija setup. Co-Authored-By: Claude Fable 5 --- .dockerignore | 10 ++++++++++ .gitignore | 1 + Dockerfile | 33 +++++++++++++++++++++++++++++++++ deploy/redeploy-tefter.sh | 30 ++++++++++++++++++++++++++++++ deploy/tefter.service | 20 ++++++++++++++++++++ docker-compose.yml | 20 ++++++++++++++++++++ 6 files changed, 114 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100755 deploy/redeploy-tefter.sh create mode 100644 deploy/tefter.service create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..64c11c5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +.git +.gitea +build +desktop +frontend/node_modules +frontend/dist +server/webdist/dist +notes.zip +*.md +.env.docker diff --git a/.gitignore b/.gitignore index 7946580..9076ffd 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ desktop/build/bin/ *.db-wal *.db-shm notes.zip +.env.docker !server/webdist/dist/.gitkeep diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e44354e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,33 @@ +# Build the frontend bundle (vite), then the static tefterd binary that +# embeds it, then ship binary-only. VERSION flows in from compose +# (${TEFTER_VERSION}) so `tefterd version` matches the git describe. + +# Fully-qualified images (podman enforces short-name resolution non-interactively). +FROM docker.io/library/node:22-alpine AS frontend +WORKDIR /src/frontend +COPY frontend/package.json frontend/package-lock.json ./ +RUN npm ci --no-audit --no-fund +COPY frontend/ ./ +ARG VERSION=dev +RUN TEFTER_VERSION=$VERSION npx vite build + +FROM docker.io/library/golang:1.25-alpine AS build +WORKDIR /src/server +COPY server/go.mod server/go.sum ./ +RUN go mod download +COPY server/ ./ +# go:embed cannot reach outside the module dir, so the bundle is copied in +# (same as the Makefile does). +COPY --from=frontend /src/frontend/dist ./webdist/dist +ARG VERSION=dev +RUN CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X main.Version=$VERSION" -o /tefterd . + +FROM docker.io/library/alpine:3.22 +RUN adduser -D -H tefter && mkdir -p /data && chown tefter /data +COPY --from=build /tefterd /usr/local/bin/tefterd +USER tefter +ENV TEFTER_DB=/data/tefter.db +# The auth token is generated and printed to the logs on first start. +EXPOSE 8420 +VOLUME /data +CMD ["tefterd"] diff --git a/deploy/redeploy-tefter.sh b/deploy/redeploy-tefter.sh new file mode 100755 index 0000000..f04e5ad --- /dev/null +++ b/deploy/redeploy-tefter.sh @@ -0,0 +1,30 @@ +#!/bin/bash +set -e + +APP_DIR=$HOME/tefter +SERVICE=tefter.service + +cd "$APP_DIR" + +# Port the app is published on (kept in .env.docker next to the compose file). +WEB_PORT=$(grep -oP '^WEB_PORT=\K.*' .env.docker 2>/dev/null || echo 8420) + +echo "==> Pulling latest code..." +git pull + +echo "==> Rebuilding container..." +TEFTER_VERSION=$(git describe --tags --always --dirty) podman-compose build + +echo "==> Restarting service..." +systemctl --user restart $SERVICE + +echo "==> Waiting for boot..." +sleep 5 + +if curl -s -o /dev/null -w "" http://localhost:$WEB_PORT/; then + echo "==> Deploy successful! App is running on port $WEB_PORT." + podman-compose logs --tail 5 tefter 2>&1 +else + echo "==> WARNING: App may not be ready yet. Check logs:" + podman-compose logs --tail 20 tefter 2>&1 +fi diff --git a/deploy/tefter.service b/deploy/tefter.service new file mode 100644 index 0000000..723643a --- /dev/null +++ b/deploy/tefter.service @@ -0,0 +1,20 @@ +# User unit — install to ~/.config/systemd/user/tefter.service, then: +# systemctl --user daemon-reload && systemctl --user enable --now tefter.service +# Requires lingering (loginctl enable-linger) so it survives logout. + +[Unit] +Description=Tefter notes server (podman-compose) +Wants=network-online.target +After=network-online.target + +[Service] +Type=simple +WorkingDirectory=%h/tefter +EnvironmentFile=%h/tefter/.env.docker +ExecStart=/home/hamo/.local/bin/podman-compose up +ExecStop=/home/hamo/.local/bin/podman-compose down +Restart=always +RestartSec=10 + +[Install] +WantedBy=default.target diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..8a5d649 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,20 @@ +services: + tefter: + build: + context: . + args: + VERSION: ${TEFTER_VERSION:-dev} + environment: + TEFTER_DB: /data/tefter.db + volumes: + # ":Z" relabels for SELinux (required on the podman/SELinux server; a + # harmless no-op on non-SELinux Docker hosts). + - data:/data:Z + security_opt: + - label:disable + ports: + - "${WEB_PORT:-8420}:8420" + restart: unless-stopped + +volumes: + data: