build: split into 3 single-process images, drop nginx

Postiz upstream ships a single image bundling backend+frontend+orchestrator
behind an in-container nginx multiplexer on :5000. We don't run nginx —
ever — in our k8s stack. Replace with three single-process images:

- ghcr.io/hanzoai/social-backend      — NestJS, listens on :3000
- ghcr.io/hanzoai/social-frontend     — Next.js SSR, listens on :4200
- ghcr.io/hanzoai/social-orchestrator — Temporal worker, no HTTP

Dockerfile is multi-target (target=backend|frontend|orchestrator). One
shared build stage runs pnpm install + pnpm run build, then three runtime
stages COPY the built workspace and pick the right pnpm --filter at CMD.

docker-publish.yml fans out a matrix across the three targets — same
shared workflow, three published images.

Deleted: Dockerfile.dev (nginx-bundled), var/docker/nginx.conf.
This commit is contained in:
zeekay
2026-06-10 21:39:28 -07:00
parent 27045bf370
commit 2abcbd752e
4 changed files with 71 additions and 100 deletions
+12 -2
View File
@@ -16,10 +16,20 @@ on:
jobs:
docker:
strategy:
matrix:
include:
- image: ghcr.io/hanzoai/social-backend
target: backend
- image: ghcr.io/hanzoai/social-frontend
target: frontend
- image: ghcr.io/hanzoai/social-orchestrator
target: orchestrator
uses: hanzoai/.github/.github/workflows/docker-build.yml@main
with:
image: ghcr.io/hanzoai/social
dockerfile: Dockerfile.dev
image: ${{ matrix.image }}
dockerfile: Dockerfile
target: ${{ matrix.target }}
platforms: linux/amd64
runner-amd64: hanzo-build-linux-amd64
build-args: |
+59
View File
@@ -0,0 +1,59 @@
# syntax=docker/dockerfile:1.7
# Hanzo Social — production multi-target build.
#
# Three runtime targets, one per app. No nginx, no pm2 — each container runs
# a single Node process. Cluster ingress (hanzoai/ingress, traefik) does
# the path-based routing across them.
#
# Build all three:
# docker buildx build --target backend -t ghcr.io/hanzoai/social-backend .
# docker buildx build --target frontend -t ghcr.io/hanzoai/social-frontend .
# docker buildx build --target orchestrator -t ghcr.io/hanzoai/social-orchestrator .
ARG NODE_VERSION=22.20-bookworm-slim
ARG NEXT_PUBLIC_VERSION=dev
# ─── Stage 1: build (shared across all three apps) ───────────────────
FROM node:${NODE_VERSION} AS build
ARG NEXT_PUBLIC_VERSION
ENV NEXT_PUBLIC_VERSION=$NEXT_PUBLIC_VERSION
WORKDIR /app
RUN apt-get update \
&& apt-get install -y --no-install-recommends g++ make python3-pip \
&& rm -rf /var/lib/apt/lists/*
RUN npm --no-update-notifier --no-fund --global install pnpm@10.6.1
COPY . /app
RUN pnpm install --frozen-lockfile
RUN NODE_OPTIONS="--max-old-space-size=4096" pnpm run build
# ─── Stage 2a: backend (NestJS, port 3000) ───────────────────────────
FROM node:${NODE_VERSION} AS backend
ENV NODE_ENV=production
WORKDIR /app
RUN npm --no-update-notifier --no-fund --global install pnpm@10.6.1
COPY --from=build /app /app
EXPOSE 3000
# Run prisma db push on startup so schema drift is reconciled before serving.
# (Postiz upstream pattern — see root package.json "pm2-run".)
CMD ["sh", "-c", "pnpm run prisma-db-push && pnpm --filter ./apps/backend start"]
# ─── Stage 2b: frontend (Next.js SSR, port 4200) ─────────────────────
FROM node:${NODE_VERSION} AS frontend
ENV NODE_ENV=production
WORKDIR /app
RUN npm --no-update-notifier --no-fund --global install pnpm@10.6.1
COPY --from=build /app /app
EXPOSE 4200
CMD ["sh", "-c", "pnpm --filter ./apps/frontend start"]
# ─── Stage 2c: orchestrator (Temporal worker, no HTTP port) ──────────
FROM node:${NODE_VERSION} AS orchestrator
ENV NODE_ENV=production
WORKDIR /app
RUN npm --no-update-notifier --no-fund --global install pnpm@10.6.1
COPY --from=build /app /app
CMD ["sh", "-c", "pnpm --filter ./apps/orchestrator start"]
-28
View File
@@ -1,28 +0,0 @@
FROM node:22.20-bookworm-slim
ARG NEXT_PUBLIC_VERSION
ENV NEXT_PUBLIC_VERSION=$NEXT_PUBLIC_VERSION
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
make \
python3-pip \
bash \
nginx \
&& rm -rf /var/lib/apt/lists/*
RUN addgroup --system www \
&& adduser --system --ingroup www --home /www --shell /usr/sbin/nologin www \
&& mkdir -p /www \
&& chown -R www:www /www /var/lib/nginx
RUN npm --no-update-notifier --no-fund --global install pnpm@10.6.1 pm2
WORKDIR /app
COPY . /app
COPY var/docker/nginx.conf /etc/nginx/nginx.conf
RUN pnpm install
RUN NODE_OPTIONS="--max-old-space-size=4096" pnpm run build
CMD ["sh", "-c", "nginx && pnpm run pm2"]
-70
View File
@@ -1,70 +0,0 @@
user www;
worker_processes auto; # it will be determinate automatically by the number of core
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
access_log /var/log/nginx/access.log;
client_max_body_size 2G;
server {
listen 5000;
server_name _;
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
location /api/ {
proxy_pass http://localhost:3000/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Reload $http_reload;
proxy_set_header Onboarding $http_onboarding;
proxy_set_header Activate $http_activate;
proxy_set_header Auth $http_auth;
proxy_set_header Showorg $http_showorg;
proxy_set_header Impersonate $http_impersonate;
proxy_set_header Accept-Language $http_accept_language;
}
location /uploads/ {
alias /uploads/;
add_header X-Content-Type-Options "nosniff" always;
add_header Content-Security-Policy "default-src 'none'; img-src 'self'; media-src 'self'; style-src 'none'; script-src 'none'; frame-ancestors 'none'; sandbox" always;
types {
image/jpeg jpg jpeg;
image/png png;
image/gif gif;
image/webp webp;
image/avif avif;
image/bmp bmp;
image/tiff tif tiff;
video/mp4 mp4;
}
default_type application/octet-stream;
}
location / {
proxy_pass http://localhost:4200/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Reload $http_reload;
proxy_set_header Onboarding $http_onboarding;
proxy_set_header Activate $http_activate;
proxy_set_header Auth $http_auth;
proxy_set_header Showorg $http_showorg;
proxy_set_header Impersonate $http_impersonate;
proxy_set_header Accept-Language $http_accept_language;
proxy_set_header i18next $http_i18next;
}
}
}