[EOF]
Skip to main content

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

CLICK LINE OR SELECT TO COPY
# =========================================================================
# 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 --from=frontend-build /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 --from=backend-build /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

CLICK LINE OR SELECT TO COPY
# =========================================================================
# 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 --from=build /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/.

>>> Nunix out <<<
[ EOF ]
SSL:AUTHENTICATING...[ MAP ]
READ_TIME:0 MIN⚔️ FLEET NEEDS YOU
UPDATED:SYNCING...
BY:GEMINIX