docker-compose.prod.yml
Rama main · réplica de lectura del repositorio original.
# ── NineDeploy standalone container deployment ────────────────────────────
# Boots the production panel image with the host Docker socket mounted, so
# deployed apps, managed databases and the Traefik ingress container are all
# managed as host siblings through the socket. No systemd, no host Node.js.
#
# Easiest: let the installer manage this file and its .env for you —
# ./install.sh --docker (or: curl … install.sh | bash -s -- --docker)
#
# Manually:
# echo "NINEDEPLOY_JWT_SECRET=$(openssl rand -hex 32)" >> .env
# echo "DOCKER_GID=$(getent group docker | cut -d: -f3)" >> .env
# docker compose -f docker-compose.prod.yml up -d
#
# The panel answers on http://<host>:3000. Domains attached to services are
# served by the ninedeploy-traefik sibling container on :80/:443 — keep those
# ports free on the host. PM2-type services and UFW firewall management need
# the bare-metal installer instead (see docs/QUICKSTART.md).
#
# The image runs as the non-root `ninedeploy` user; the host socket is
# typically root:docker mode 0660, so the docker group's GID is required.
#
# Values are read from a .env file next to this compose file (docker compose
# loads it automatically). The installer (install.sh --docker) maintains that
# file for you; manually, create it with at least:
#
# NINEDEPLOY_JWT_SECRET=<openssl rand -hex 32>
# DOCKER_GID=$(getent group docker | cut -d: -f3)
name: ninedeploy
services:
ninedeploy:
# r571: the installer pins the release it resolved in .env
# (NINEDEPLOY_IMAGE_TAG=vX.Y.Z); without it this tracks :latest, the
# newest release. To upgrade by hand: set NINEDEPLOY_IMAGE_TAG to the new
# tag in .env, then docker compose pull && docker compose up -d — or just
# re-run the installer, which resolves and writes it for you.
image: ghcr.io/ninedeploy/ninedeploy:${NINEDEPLOY_IMAGE_TAG:-latest}
# Or build locally: docker compose -f docker-compose.prod.yml build --no-cache
# build:
# context: .
# dockerfile: Dockerfile
restart: unless-stopped
# The deploy worker's shutdown grace is 60 s: a `compose up -d` update
# SIGKILLs at the 10 s default, killing in-flight builds mid-step and
# leaving their services undeployable until the 45-minute staleness
# sweep. 90 s lets deploys checkpoint-cancel cleanly first.
stop_grace_period: 90s
ports:
# Host-side port is variable; the container always listens on 3000.
# Bound to LOOPBACK when NINEDEPLOY_BIND is unset: this port speaks
# plain HTTP, and login plus bearer/refresh tokens cross it. Since 0.16
# the installer writes NINEDEPLOY_BIND=0.0.0.0 on FRESH installs (not
# with --private) so http://<server-ip>:3000 works out of the box, and
# gates the first admin on NINEDEPLOY_SETUP_TOKEN. Keep it off 127.0.0.1
# while the panel has a domain: Traefik reaches it through the Docker
# gateway on host port 3000. Block the port from the internet with a
# cloud firewall instead (published ports bypass ufw).
- "${NINEDEPLOY_BIND:-127.0.0.1}:${NINEDEPLOY_PORT:-3000}:3000"
environment:
NODE_ENV: production
# A strong secret is mandatory — production refuses to boot on the
# known defaults (see apps/server/src/env.ts).
NINEDEPLOY_JWT_SECRET: "${NINEDEPLOY_JWT_SECRET:?generate one: openssl rand -hex 32}"
NINEDEPLOY_PUBLIC_URL: ${NINEDEPLOY_PUBLIC_URL:-http://localhost:3000}
# One-time token the first-run admin form requires (0.16; empty = the
# panel generates one in /data/setup-token and logs it).
NINEDEPLOY_SETUP_TOKEN: ${NINEDEPLOY_SETUP_TOKEN:-}
# Optional integrations (set in .env; empty values are treated as unset):
NINEDEPLOY_ACME_EMAIL: ${NINEDEPLOY_ACME_EMAIL:-}
NINEDEPLOY_DNS_PROVIDER: ${NINEDEPLOY_DNS_PROVIDER:-}
NINEDEPLOY_DNS_TOKEN: ${NINEDEPLOY_DNS_TOKEN:-}
# "stable" offers only releases on the stable tag (install.sh --channel
# stable writes it). Settings → System → Update channel wins over it.
NINEDEPLOY_UPDATE_CHANNEL: ${NINEDEPLOY_UPDATE_CHANNEL:-}
# A2: the panel's own hostname (install.sh --domain). Traefik routes it to
# the panel, with a Let's Encrypt certificate once NINEDEPLOY_ACME_EMAIL is
# set. Settings → Security → Panel domain wins over it.
NINEDEPLOY_DOMAIN: ${NINEDEPLOY_DOMAIN:-}
NINEDEPLOY_PUBLIC_IP: ${NINEDEPLOY_PUBLIC_IP:-}
# "off" ignores the panel IP allow-list (Settings → Security), for when
# you have locked yourself out. Remove it again once the list is fixed.
NINEDEPLOY_PANEL_ACCESS: ${NINEDEPLOY_PANEL_ACCESS:-on}
volumes:
- /var/run/docker.sock:/var/run/docker.sock
# SQLite db, git checkouts, encrypted backups and master.key live here.
- ninedeploy-data:/data
group_add:
- "${DOCKER_GID:?set it: getent group docker | cut -d: -f3}"
volumes:
ninedeploy-data: