Containers
Two multi-stage Containerfiles, each carrying a dev target (hot reload against a bind-mounted source tree) and a prod target (baked, minimal runtime image) off the same dependency-install layer. Full story in The Forge and the Quadlet.
Command Center Containerfile
A Go + Vite service — frontend-dev/backend-dev targets for hot-reload against bind-mounted source, prod for a minimal embedded-frontend binary (Go's go:embed bakes the built React app straight into the binary).
Gist: https://gist.github.com/nunix/b62a4f3863135313349640bd096c3e72
# =========================================================================
# fleet-v3 command center — multi-stage Containerfile
# Targets: frontend-dev (frontend hot reload), backend-dev (go run hot reload),
# prod (default — embeds built frontend into Go binary via go:embed)
# =========================================================================
# --- Frontend deps (shared by dev + build) ---------------------------------
FROM registry.suse.com/bci/nodejs:20 AS frontend-base
WORKDIR /app
COPY frontend/package*.json ./
# --legacy-peer-deps: repo has an [email protected] vs [email protected]
# peer-dep mismatch (devDependencies only, pre-existing in package.json,
# not introduced here). Standard npm flag, not a workaround script.
RUN npm install --legacy-peer-deps
# --- Frontend DEV target: hot reload against bind-mounted source -----------
# Run with source bind-mounted at /app (do NOT bake source into image).
# Example: podman run -v ./frontend:/app:Z -p 5174:5174 --target frontend-dev
FROM frontend-base AS frontend-dev
EXPOSE 5174
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0", "--port", "5174"]
# --- Frontend BUILD stage (prod path) ---------------------------------------
FROM frontend-base AS frontend-build
COPY frontend/ ./
RUN npm run build
# vite.config.js build.outDir = '../backend/dist' -> resolves to /backend/dist
# in this stage's filesystem (WORKDIR is /app, sibling to /backend). This is
# intentional, verified working — do not "fix" this path.
# --- Backend DEV target: go run against bind-mounted source, no image bake --
# go.mod requires go 1.25.9 -> golang:1.25 satisfies it (verified pullable
# from registry.suse.com).
FROM registry.suse.com/bci/golang:1.25 AS backend-dev
WORKDIR /app
EXPOSE 5173
CMD ["go", "run", "."]
# --- Backend BUILD stage (prod path) ----------------------------------------
FROM registry.suse.com/bci/golang:1.25 AS backend-build
WORKDIR /app
COPY backend/go.mod backend/go.sum ./
RUN go mod download
COPY backend/ ./
COPY /app/../backend/dist ./dist
RUN go build -o fleet-v3 .
# --- PROD runtime — SLE Micro minimal (default final target) ---------------
FROM registry.suse.com/bci/bci-micro:latest AS prod
WORKDIR /app
COPY /app/fleet-v3 .
EXPOSE 5173
CMD ["./fleet-v3"]
Docusaurus Blog Containerfile
Bun-based multi-stage build — dev for hot reload against a bind-mounted repo, prod serving a static build via nginx.
Gist: https://gist.github.com/nunix/66eaae5ca0f8114d40deb90b36b58ad5
# =========================================================================
# nunix-blog (Docusaurus 3.9.2 + React 19) — multi-stage Containerfile
# Targets: dev (hot reload against bind-mounted source), build, prod (default)
# =========================================================================
# --- deps: bun install (repo has bun.lock, package-lock.json also present
# but bun install reads package.json directly regardless) -------------------
FROM docker.io/oven/bun:1 AS deps
WORKDIR /app
COPY package.json bun.lock* ./
RUN bun install --frozen-lockfile
# --- DEV target: hot reload against bind-mounted source ---------------------
# Run with source bind-mounted at /app (do NOT bake source into image).
# Example: podman run -v .:/app:Z -p 3000:3000 --target dev
FROM deps AS dev
WORKDIR /app
EXPOSE 3000
CMD ["bun", "run", "start"]
# --- BUILD stage -------------------------------------------------------------
FROM deps AS build
WORKDIR /app
COPY . .
RUN bun run build
# --- PROD runtime — nginx:alpine (tiny, universally known, zero config
# needed beyond static file serving + SPA-style fallback for Docusaurus
# client routing; chosen over Caddy since Docusaurus build output is plain
# static HTML/JS with no need for Caddy's auto-TLS/reverse-proxy features) --
FROM docker.io/library/nginx:alpine AS prod
COPY /app/build /usr/share/nginx/html
RUN sed -i 's/listen\s*80;/listen 3000;/' /etc/nginx/conf.d/default.conf \
&& sed -i 's/listen\s*\[::\]:80;/listen [::]:3000;/' /etc/nginx/conf.d/default.conf || true
EXPOSE 3000
CMD ["nginx", "-g", "daemon off;"]
Canonical copies also live in nunix/config under armory/imperium-setup/.