refactor: use BSB container + mount sec-config at runtime

Dockerfile.server now uses betterweb/service-base:node as runtime
base instead of node:24-trixie-slim + manual bsb-plugin-cli. BSB
container handles entrypoint, user, plugin loading.

sec-config.yaml removed from Docker image — must be bind-mounted
at /app/sec-config.yaml. Both compose files updated with :ro mount.
All BF_* env vars removed from compose server service.

deploy/docker/sec-config.yaml deleted (was baked in, now mounted).
version.ts path updated for new workdir /app.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mitchell R 2026-05-23 23:56:44 +02:00
parent 49d730cf7f
commit 34331c9d0e
No known key found for this signature in database
5 changed files with 29 additions and 114 deletions

View file

@ -1,11 +1,16 @@
# BetterFrame server image — Node 24 + native deps for argon2/sqlite. # BetterFrame server — BSB container with built plugins.
# Trixie base (Debian 13, latest stable) — matches the host distro we #
# recommend in deploy/README.md. # sec-config.yaml is NOT baked in — mount it at runtime:
# volumes:
# - ./sec-config.yaml:/app/sec-config.yaml:ro
#
# Builder stage compiles TS + native deps (argon2).
# Runtime stage uses the official BSB container.
FROM node:24-trixie-slim AS builder FROM node:24-trixie-slim AS builder
WORKDIR /app WORKDIR /app
# Build deps for argon2 + bsb-plugin-cli
RUN apt-get update && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential python3 \ build-essential python3 \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
@ -17,48 +22,40 @@ RUN npm ci && npm rebuild argon2
COPY server ./server COPY server ./server
# Run BSB build — extracts schemas + compiles TS + generates plugin manifests
WORKDIR /app/server WORKDIR /app/server
RUN npm run build RUN npm run build
# ---- Runtime image ---- # ---- Runtime ----
FROM node:24-trixie-slim FROM betterweb/service-base:node
# Version baked at build time. Coolify doesn't include .git in build context
# so we can't derive from git inside Docker. Instead, compose passes it as a
# build arg from SOURCE_COMMIT / COOLIFY_GIT_COMMIT env vars that Coolify
# DOES inject into the build environment.
ARG BF_SERVER_VERSION=dev ARG BF_SERVER_VERSION=dev
# ffmpeg for camera snapshot capture (optional but needed for /admin/entities/:id/snapshot) USER root
# ffmpeg for camera snapshot capture
RUN apt-get update && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates ffmpeg wget \ ffmpeg \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
RUN useradd -m -d /var/lib/betterframe -s /bin/false betterframe RUN mkdir -p /var/lib/betterframe && chown node:node /var/lib/betterframe
WORKDIR /app WORKDIR /app
# Copy built plugin + deps
COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/server ./server COPY --from=builder /app/server/lib ./lib
COPY --from=builder /app/tsconfig.base.json ./ COPY --from=builder /app/server/bsb-plugin.json ./bsb-plugin.json
COPY --from=builder /app/package.json ./ COPY --from=builder /app/server/package.json ./package.json
COPY --from=builder /app/tsconfig.base.json ./tsconfig.base.json
# Default sec-config baked into image. BF_* env vars in compose override at # Static web assets served by admin-http
# runtime (see shared/env-overrides.ts). No host bind mount needed. COPY --from=builder /app/server/lib/web-static ./lib/web-static
COPY deploy/docker/sec-config.yaml /app/server/sec-config.yaml
# Bake version into a file readable by version.ts at runtime. # Bake version
RUN echo "$BF_SERVER_VERSION" > /app/server/.bf-version RUN echo "$BF_SERVER_VERSION" > /app/.bf-version
RUN mkdir -p /var/lib/betterframe && chown betterframe:betterframe /var/lib/betterframe
VOLUME /var/lib/betterframe VOLUME /var/lib/betterframe
EXPOSE 18080 18081 18082 EXPOSE 18080 18081 18082
USER betterframe USER node
WORKDIR /app/server
ENV NODE_OPTIONS=--import=tsx
CMD ["node", "--import", "tsx", "/app/node_modules/@bsb/base/lib/scripts/bsb-plugin-cli.js", "start"]

View file

@ -1,65 +0,0 @@
# BSB runtime configuration for the Docker compose stack.
# Backend services bind all interfaces inside the private compose network;
# Angie/nginx is the only published host port.
default:
observable:
observable-default:
plugin: observable-default
enabled: true
config: {}
events:
events-default:
plugin: events-default
enabled: true
services:
service-store:
plugin: service-store
enabled: true
config:
sqlitePath: /var/lib/betterframe/betterframe.db
service-admin-http:
plugin: service-admin-http
enabled: true
config:
host: 0.0.0.0
port: 18080
dataDir: /var/lib/betterframe
sessionIdleSeconds: 43200
sessionMaxSeconds: 2592000
loginLockoutThreshold: 8
loginLockoutSeconds: 900
argon2Memory: 65536
argon2TimeCost: 3
argon2Parallelism: 2
cookieName: betterframe_session
totpIssuer: BetterFrame
noderedUrl: http://nodered:1880
selfUrl: http://server:18080
service-api-http:
plugin: service-api-http
enabled: true
config:
host: 0.0.0.0
port: 18081
codeTtlSeconds: 600
dataDir: /var/lib/betterframe
argon2Memory: 65536
argon2TimeCost: 3
argon2Parallelism: 2
noderedUrl: http://nodered:1880
service-coordinator-ws:
plugin: service-coordinator-ws
enabled: true
config:
host: 0.0.0.0
port: 18082
noderedUrl: http://nodered:1880
dataDir: /var/lib/betterframe
cookieName: betterframe_session
argon2Memory: 65536
argon2TimeCost: 3
argon2Parallelism: 2

View file

@ -25,6 +25,7 @@ services:
- TZ=UTC - TZ=UTC
volumes: volumes:
- betterframe-data:/var/lib/betterframe - betterframe-data:/var/lib/betterframe
- ./sec-config.yaml:/app/sec-config.yaml:ro
expose: expose:
- "18080" - "18080"
- "18081" - "18081"

View file

@ -31,29 +31,11 @@ services:
BF_SERVER_VERSION: ${BF_SERVER_VERSION:-} BF_SERVER_VERSION: ${BF_SERVER_VERSION:-}
container_name: betterframe-server container_name: betterframe-server
restart: unless-stopped restart: unless-stopped
# Env overrides win over sec-config.yaml — Coolify / k8s inject these.
environment: environment:
- TZ=UTC - TZ=UTC
- BF_DATA_DIR=/var/lib/betterframe
- BF_SQLITE_PATH=/var/lib/betterframe/betterframe.db
- BF_NODERED_URL=http://nodered:1880
- BF_SELF_URL=http://server:18080
- BF_SERVER_VERSION=${BF_SERVER_VERSION:-}
- COOLIFY_GIT_COMMIT=${COOLIFY_GIT_COMMIT:-}
- SOURCE_COMMIT=${SOURCE_COMMIT:-}
# Optional: paste Ed25519 PEM private key here for firmware signing.
# - BF_FIRMWARE_SIGNING_KEY=
# Optional: single-purpose Bearer token for GitHub Actions firmware import.
# Set GitHub's BF_AUTOIMPORT_API_KEY secret to the same value.
# Generate with: openssl rand -base64 32
# - BF_FIRMWARE_IMPORT_API_KEY=
# Optional MQTT telemetry bridge (ThingsBoard / HA / Influx / etc).
# - BF_MQTT_URL=mqtt://broker:1883
# - BF_MQTT_USERNAME=
# - BF_MQTT_PASSWORD=
# - BF_MQTT_TOPIC_PREFIX=betterframe
volumes: volumes:
- betterframe-data:/var/lib/betterframe - betterframe-data:/var/lib/betterframe
- ./sec-config.yaml:/app/sec-config.yaml:ro
expose: expose:
- "18080" - "18080"
- "18081" - "18081"

View file

@ -5,7 +5,7 @@ let cached: string | null = null;
export function serverVersion(): string { export function serverVersion(): string {
if (cached) return cached; if (cached) return cached;
try { try {
const v = readFileSync("/app/server/.bf-version", "utf8").trim(); const v = readFileSync("/app/.bf-version", "utf8").trim();
cached = v && v !== "dev" ? v : "dev"; cached = v && v !== "dev" ? v : "dev";
} catch { } catch {
cached = "dev"; cached = "dev";