Spaces:
Running
Running
File size: 9,826 Bytes
b4d7c1c 866a8e6 b4d7c1c 866a8e6 b4d7c1c 866a8e6 a83da60 a1ea643 b4d7c1c 866a8e6 b4d7c1c 866a8e6 b4d7c1c 2ecb798 6c0455b 2ecb798 866a8e6 2ecb798 866a8e6 2ecb798 c63d0d1 8de0b02 7fec572 2ecb798 b4d7c1c c63d0d1 8de0b02 a1ea643 7fec572 8de0b02 866a8e6 8de0b02 b4d7c1c 866a8e6 b4d7c1c 866a8e6 b4d7c1c 866a8e6 b4d7c1c 866a8e6 0394ebe b4d7c1c c4c4e4c 57b8b04 0394ebe 57b8b04 0394ebe b4d7c1c a1ea643 b4d7c1c 5d2b465 b4d7c1c fd4cae0 5d2b465 b4d7c1c 866a8e6 b4d7c1c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | # ============================================================================
# HuggingPost β Postiz v2.11.3 on Hugging Face Spaces
#
# Two-stage build: compile only the server-side apps (backend/workers/cron)
# during docker build. The Next.js frontend is intentionally NOT built here.
#
# Why: `next build` for Postiz needs ~4 GB RSS. The HF Space builder has a
# ~4 GB cgroup limit, so it always OOMKills the process (exit 137) regardless
# of heap tuning, parallel/single-thread settings, or multi-stage tricks.
#
# Solution: build the frontend in start.sh at container startup, where the
# runtime has 16 GB RAM. The compiled .next is included in the HF Dataset
# backup so subsequent restarts skip the build entirely.
#
# First boot: ~5-8 min (server apps start immediately; frontend compiles in
# background, then Postiz frontend process starts when done).
# Later boots: .next restored from backup β Postiz starts normally (~90 s).
#
# Container layout at runtime:
# - nginx (port 5000, internal) β Postiz frontend + backend + uploads
# - PM2 β 4 Postiz procs (backend / frontend / workers / cron)
# - postgres (port 5432, internal)
# - redis (port 6379, internal)
# - postiz-sync.py loop β backup DB + uploads + .next
# - health-server.js (port 7860, public) β dashboard + reverse proxy
# ============================================================================
# ββ Stage 1: Clone, patch, install deps, build server apps βββββββββββββββββββ
FROM node:22.20-alpine AS postiz-builder
WORKDIR /build
ARG NEXT_PUBLIC_VERSION=v2.11.3
ENV NEXT_PUBLIC_VERSION=$NEXT_PUBLIC_VERSION
RUN apk add --no-cache \
git \
g++ \
make \
py3-pip \
bash
RUN npm install -g pnpm@10.6.1
# Pinned to v2.11.3 β last release before Temporal became a hard requirement.
RUN git clone --depth=1 --branch v2.11.3 https://github.com/gitroomhq/postiz-app.git .
ENV SENTRY_DSN="" \
SENTRY_AUTH_TOKEN="" \
SENTRY_ORG="" \
SENTRY_PROJECT="" \
NEXT_PUBLIC_SENTRY_DSN="" \
NEXT_TELEMETRY_DISABLED=1 \
NEXT_PRIVATE_SKIP_SIZE_MINIMIZATION=true
# Install deps BEFORE patching so the install layer is cached independently.
# Patches are sed commands on JS files β they don't affect node_modules.
# Changing patches won't bust the pnpm install cache layer.
RUN pnpm install --frozen-lockfile=false
# Vendor Plus Jakarta Sans β HF Space cannot reach fonts.gstatic.com.
# Postiz upstream imports `Plus_Jakarta_Sans` from `next/font/google` in two
# layout files, which forces a build-time fetch from gstatic. HF egress to
# gstatic is blocked/throttled, so `next build` retry-loops indefinitely.
# We ship 4 woff2 variants in the image and rewrite both layouts to use
# `next/font/local` (zero network at build).
COPY vendor/fonts/PlusJakartaSans-500-normal.woff2 \
vendor/fonts/PlusJakartaSans-500-italic.woff2 \
vendor/fonts/PlusJakartaSans-600-normal.woff2 \
vendor/fonts/PlusJakartaSans-600-italic.woff2 \
apps/frontend/src/fonts/
COPY vendor/patch-jakarta-font.js /tmp/patch-jakarta-font.js
RUN node /tmp/patch-jakarta-font.js \
&& grep -q "next/font/local" "apps/frontend/src/app/(app)/layout.tsx" \
&& grep -q "next/font/local" "apps/frontend/src/app/(extension)/layout.tsx" \
&& ! grep -rq "next/font/google" apps/frontend/src/app/ \
|| (echo "JAKARTA PATCH FAILED β layout.tsx shape changed upstream"; exit 1)
# Patch Next.js config (after install β see above).
# 1. basePath/assetPrefix=/app β Postiz UI at /app; HuggingPost dashboard owns /
# 2. productionBrowserSourceMaps: false β smaller build output
# 3. Sentry sourcemap plugin disabled β no network calls during build
# 4. swcMinify: false β Terser (pure JS) instead of native SWC binary
# 5. experimental.cpus=1 + workerThreads=false β single-thread webpack
# 6. images.unoptimized: true β skip _next/image optimizer (fails with basePath)
RUN sed -i "s|const nextConfig = {|const nextConfig = {\n basePath: '/app',\n assetPrefix: '/app',\n trailingSlash: true,\n swcMinify: false,|" apps/frontend/next.config.js \
&& sed -i "s|productionBrowserSourceMaps: true|productionBrowserSourceMaps: false|" apps/frontend/next.config.js \
&& sed -i "s|disable: false,|disable: true,|" apps/frontend/next.config.js \
&& sed -i "s|experimental: {|experimental: {\n cpus: 1,\n workerThreads: false,|" apps/frontend/next.config.js \
&& (grep -q 'images:' apps/frontend/next.config.js \
&& sed -i 's|images: {|images: {\n unoptimized: true,|' apps/frontend/next.config.js \
|| sed -i "s|const nextConfig = {|const nextConfig = {\n images: { unoptimized: true },|" apps/frontend/next.config.js) \
&& grep -q "basePath: '/app'" apps/frontend/next.config.js \
&& grep -q "trailingSlash: true" apps/frontend/next.config.js \
&& grep -q "productionBrowserSourceMaps: false" apps/frontend/next.config.js \
&& grep -q "swcMinify: false" apps/frontend/next.config.js \
&& grep -q "cpus: 1" apps/frontend/next.config.js \
|| (echo "PATCH FAILED β next.config.js shape changed upstream"; exit 1)
# Build server-side apps. Sequential + 3 GB heap each.
# Frontend is NOT built here β see start.sh.
RUN NODE_OPTIONS="--max-old-space-size=3072" pnpm run build:backend
RUN NODE_OPTIONS="--max-old-space-size=3072" pnpm run build:workers
RUN NODE_OPTIONS="--max-old-space-size=3072" pnpm run build:cron
# Clean up dev artefacts before Stage 2 copies this tree into the runtime image.
RUN find . -name ".git" -type d -prune -exec rm -rf {} + 2>/dev/null || true \
&& rm -rf .github reports Jenkins .devcontainer 2>/dev/null || true
# ββ Stage 2: Runtime ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
FROM node:22.20-alpine
WORKDIR /app
RUN apk add --no-cache \
bash \
curl \
ca-certificates \
openssl \
jq \
nginx \
postgresql16 \
postgresql16-contrib \
postgresql16-client \
redis \
py3-pip \
su-exec
# nginx user β upstream uses 'www'. Mirror that so its nginx.conf works.
RUN adduser -D -g 'www' www \
&& mkdir -p /var/lib/nginx /var/log/nginx \
&& chown -R www:www /var/lib/nginx
RUN npm install -g pnpm@10.6.1 pm2
RUN pip install --no-cache-dir --break-system-packages \
huggingface_hub \
PyYAML
# Copy fully-built Postiz server apps + node_modules + patched next.config.js.
# .next is intentionally absent here; start.sh builds or restores it at boot.
COPY --from=postiz-builder /build /app
# nginx.conf: routes /apiβ3000, /uploadsβfs, /β4200.
# Patch: re-add /app prefix before proxying to Next.js (port 4200) because:
# health-server strips /app from incoming /app/* requests before forwarding
# to nginx. Next.js is built with basePath="/app" so it expects /app/* paths.
# Without the patch, nginx sends /auth/login β Next.js returns 404.
# With the patch, nginx sends /app/auth/login β Next.js handles it correctly.
COPY --from=postiz-builder /build/var/docker/nginx.conf /etc/nginx/nginx.conf
# Patch 1: re-add /app basePath when proxying to Next.js (port 4200).
# health-server strips /app before forwarding to nginx, but Next.js is built
# with basePath="/app" so it expects paths prefixed with /app.
# Patch 2: add an explicit location = / block that redirects to /app/ so
# the bare root doesn't return an empty 200 from nginx's default handler.
# (health-server already short-circuits /app/ before reaching nginx, but
# this makes nginx self-consistent for any direct curl / health checks.)
RUN sed -i \
's|proxy_pass http://127.0.0.1:4200/;|proxy_pass http://127.0.0.1:4200/app/;|; \
s|proxy_pass http://localhost:4200/;|proxy_pass http://localhost:4200/app/;|; \
s|proxy_set_header X-Forwarded-Proto \$scheme;|proxy_set_header X-Forwarded-Proto \$http_x_forwarded_proto;|g' \
/etc/nginx/nginx.conf \
&& grep -q '/app/' /etc/nginx/nginx.conf \
&& grep -q 'x_forwarded_proto' /etc/nginx/nginx.conf \
|| (echo "NGINX PATCH FAILED β upstream nginx.conf format changed"; cat /etc/nginx/nginx.conf; exit 1)
# Health-server outside /app to avoid pnpm workspace collisions.
RUN mkdir -p /opt/healthsrv && cd /opt/healthsrv && \
npm init -y >/dev/null && \
npm install --no-save --no-audit --no-fund express@4 cors morgan
RUN mkdir -p /var/run/postgresql /postiz/pgdata /postiz/redis /postiz/uploads /postiz/.secrets \
&& chown -R postgres:postgres /var/run/postgresql /postiz/pgdata \
&& chmod 700 /postiz/pgdata
RUN ln -sf /postiz/uploads /uploads
COPY start.sh /opt/start.sh
COPY health-server.js /opt/healthsrv/health-server.js
COPY postiz-sync.py /opt/postiz-sync.py
COPY cloudflare-proxy.js /opt/cloudflare-proxy.js
COPY cloudflare-proxy-setup.py /opt/cloudflare-proxy-setup.py
COPY cloudflare-keepalive-setup.py /opt/cloudflare-keepalive-setup.py
# Vendor fonts + patch script available at runtime.
# Stage 1 may be cached from before the font patch was added; start.sh applies
# the patch at container start if layout.tsx still imports next/font/google.
COPY vendor/fonts/PlusJakartaSans-500-normal.woff2 \
vendor/fonts/PlusJakartaSans-500-italic.woff2 \
vendor/fonts/PlusJakartaSans-600-normal.woff2 \
vendor/fonts/PlusJakartaSans-600-italic.woff2 \
/opt/vendor/fonts/
COPY vendor/patch-jakarta-font.js /opt/vendor/patch-jakarta-font.js
RUN chmod +x /opt/start.sh /opt/cloudflare-keepalive-setup.py
EXPOSE 7860
HEALTHCHECK --interval=30s --timeout=10s --start-period=600s --retries=5 \
CMD curl -f http://localhost:7860/health || exit 1
CMD ["/opt/start.sh"]
|