Spaces:
Restarting
Restarting
| # ============================================================================ | |
| # 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"] | |