Skip to content
Q
QuoteNode

Installation

Install with Portainer, QNAP, or NAS

Deploy QuoteNode on a NAS or Portainer-managed Docker host with full setup instructions.

Install with Portainer, QNAP, or NAS

Use this path if you run Docker through Portainer, QNAP Container Station, Synology, or a similar platform. The exact UI differs by vendor, but the underlying Docker Compose stack is the same.

When to use it

  • You already operate Docker stacks through a container management UI.
  • You understand how the platform maps volumes and ports.
  • You can verify backups outside the NAS UI.

What may not work

  • Public links do not work outside the LAN unless NAT, DNS, proxy, and HTTPS are configured.
  • NAS sleep modes can interrupt service.
  • Some NAS Docker UIs rewrite compose files or environment variables — verify the rendered stack before relying on it.
  • Backups stored only on the same NAS do not protect against NAS failure.

Prerequisites

  • Docker Compose v2 support in your platform.
  • Persistent volume support for PostgreSQL data, uploads, PDFs, and backups.
  • At least 2 GB RAM available and 10 GB free disk space.
  • A stable URL: LAN IP for internal use, or a public domain behind a reverse proxy for customer links.

Step 1 — Create the configuration file

Create a .env file (or set variables through your platform’s environment UI). Replace the URL with your actual NAS address or public domain. The secret values below are filled with fresh random values in your browser on each page load — use the ↻ New secrets button on the block to regenerate them, then copy the result. (Prefer to generate them yourself? See Deployment environment variables.)

For an internal NAS-only deployment:

# Database
DB_URL=jdbc:postgresql://postgres:5432/quotenode
DB_USERNAME=quotenode
DB_PASSWORD=change-me-to-something-random-32-chars
DB_NAME=quotenode

# ============================================================
# SECURITY SECRETS — UNIQUE to this installation.
# SAVE THIS FILE and keep a secure backup.
# ============================================================
DB_ENCRYPTION_KEY=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
TIMING_TOKEN_SECRET=change-me-timing-secret-min-32-chars
PUBLIC_LINK_PASSWORD_SESSION_SECRET=change-me-session-secret-min-32

# Internal NAS URL
CORS_ALLOWED_ORIGINS=http://nas.local
DOMAIN=nas.local

# Client IP is auto-detected behind internal proxies and CDNs (Cloudflare included) — leave empty.
# Set only for an unusual proxy whose transport peer is a PUBLIC address to trust (IP/CIDR or hostname).
SECURITY_TRUSTED_PROXIES=

# Application
LOG_LEVEL=INFO
SPRING_PROFILES_ACTIVE=prod

For a public domain behind a reverse proxy:

CORS_ALLOWED_ORIGINS=https://crm.example.com
DOMAIN=crm.example.com

Save your .env immediately. If you lose DB_ENCRYPTION_KEY, encrypted fields become permanently unreadable.

Step 2 — Create the Docker Compose stack

Use the stack editor in Portainer or your NAS Docker UI, or create a docker-compose.yml file:

services:
  postgres:
    image: postgres:18-alpine
    cpus: 4.0
    mem_limit: 512m
    environment:
      POSTGRES_DB: ${DB_NAME}
      POSTGRES_USER: ${DB_USERNAME}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      PGDATA: /var/lib/postgresql/data   # required for the postgres:18 image
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USERNAME}"]
      interval: 10s
      timeout: 5s
      retries: 10
    restart: unless-stopped

  gotenberg:
    image: gotenberg/gotenberg:8
    cpus: 2.0
    mem_limit: 448m
    environment:
      LOG_LEVEL: info
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://localhost:3000/health"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  backend:
    image: ghcr.io/lesisty7/quotenode/quotenode-api:latest
    cpus: 4.0
    mem_limit: 1792m
    depends_on:
      postgres: { condition: service_healthy }
      gotenberg: { condition: service_healthy }
    env_file: .env
    environment:
      SPRING_PROFILES_ACTIVE: prod
      JOBS_MODE: web
      PDF_ENABLED: "true"
      PDF_GOTENBERG_URL: http://gotenberg:3000
    volumes:
      - backend_uploads:/app/data/uploads
      - backend_pdfs:/app/data/pdfs
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://localhost:8080/health"]
      interval: 15s
      timeout: 10s
      retries: 5
      start_period: 45s
    restart: unless-stopped

  frontend:
    image: ghcr.io/lesisty7/quotenode/quotenode-frontend:latest
    cpus: 2.0
    mem_limit: 192m
    depends_on:
      backend: { condition: service_healthy }
    ports:
      - "80:80"
      - "443:443"
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://localhost:80/"]
      interval: 15s
      timeout: 5s
      retries: 3
    restart: unless-stopped

volumes:
  postgres_data:
  backend_uploads:
  backend_pdfs:

Port conflicts: If your NAS already uses port 80 or 443, change the left side of the ports mapping (e.g. "8080:80") and update CORS_ALLOWED_ORIGINS accordingly (e.g. http://nas.local:8080).

Step 3 — Deploy and verify

Start the stack through your platform UI or via SSH:

docker compose up -d
docker compose logs -f backend

Wait until you see Started QuoteNodeApplication.

Step 4 — Open QuoteNode

Navigate to your NAS URL (e.g. http://nas.local or https://crm.example.com).

Default login credentials:

FieldValue
Email[email protected]
PasswordAdmin123!

Change the default password immediately after logging in.

Persistence checklist

Verify these are mapped to persistent volumes (not ephemeral container storage):

DataWhy it matters
PostgreSQL dataMain application database
UploadsProduct images, logos, attachments
Generated PDFsOffer and document files
BackupsLocal backup archives (if backup worker is enabled)
GeoIP databaseOptional access-control data

What is running?

ContainerWhat it doesRAM usage
postgresStores all your data~256 MB
backendJava API server, business logic~512 MB – 1.5 GB
gotenbergConverts HTML to PDF (Chromium-based)~200 MB
frontendServes the web UI + reverse proxy~20 MB

Total: approximately 1.5 – 2.5 GB RAM.

Keeping your secrets safe

SecretWhat it protectsIf lost
DB_PASSWORDDatabase accessRecoverable — reset via PostgreSQL admin tools
DB_ENCRYPTION_KEYMFA codes, SMTP password, FX API key, PII fieldsPermanently unreadable
TIMING_TOKEN_SECRETAnti-timing-attack tokensRegenerate — users re-authenticate
PUBLIC_LINK_PASSWORD_SESSION_SECRETPublic link sessionsRegenerate — active sessions expire

Store .env backups outside the NAS. A NAS failure that destroys your only copy of DB_ENCRYPTION_KEY makes encrypted data unrecoverable.

  • Configure backups to a location outside the NAS.
  • Test PDF generation after first deployment.
  • If you want a smoother public deployment path, consider Coolify on a VPS.

Last reviewed: Recently