Setup
Installation Guide
How to deploy QuoteNode on your own infrastructure using Docker Compose.
Installation
QuoteNode runs as a set of Docker containers. You do not need to install Java, Node.js, or PostgreSQL on your machine — Docker handles everything.
Prerequisites
- Docker Engine 24.0 or later (install Docker)
- Docker Compose v2 (included with modern Docker Desktop and Docker Engine)
- A machine with at least 2 GB RAM and 10 GB free disk space
To verify your setup:
docker --version # Should show 24.0+
docker compose version # Should show v2.x
Quick Start (5 minutes)
This gets QuoteNode running on your local machine for evaluation. Not for production — see below for that.
Step 1 — Create a project directory
mkdir quotenode && cd quotenode
Step 2 — Create the configuration file
Create a file named .env with the minimum required settings:
# 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 — READ THIS BEFORE CONTINUING
# These values are UNIQUE to your installation.
# If you lose this file, you CANNOT regenerate the same secrets.
# SAVE THIS FILE and keep a secure backup (or even a printout).
# ============================================================
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
# Domain (for CORS and Caddy)
CORS_ALLOWED_ORIGINS=http://localhost
DOMAIN=localhost
# Logging
LOG_LEVEL=INFO
SPRING_PROFILES_ACTIVE=prod
Save your
.envfile immediately. The secrets above were randomly generated for you and are unique to this installation. If you lose this file, you cannot regenerate the same values. Without the originalDB_ENCRYPTION_KEY, encrypted data (MFA codes, SMTP password, API keys) becomes permanently unreadable. Copy this file to a safe location — for production systems, consider printing the secrets and storing them in a physical safe.
Step 3 — Create the Docker Compose file
Create a file named docker-compose.yml:
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_PASSWORD: ${DB_PASSWORD}
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
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
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
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:
Step 4 — Start QuoteNode
docker compose up -d
Docker will download the container images (this takes a few minutes on the first run) and start all services. You can watch the startup progress with:
docker compose logs -f backend
Wait until you see a line like Started QuoteNodeApplication — this means the backend is ready.
Step 5 — Open QuoteNode
Navigate to http://localhost in your browser.
Default login credentials:
| Field | Value |
|---|---|
[email protected] | |
| Password | Admin123! |
Change the default password immediately after logging in (Settings > My Profile > Change Password).
What is running?
After docker compose up, you have 4 containers:
| Container | What it does | RAM usage |
|---|---|---|
postgres | Stores all your data | ~256 MB |
backend | Java API server, business logic | ~512 MB – 1.5 GB |
gotenberg | Converts HTML to PDF (Chromium-based) | ~200 MB |
frontend | Serves the web UI + reverse proxy | ~20 MB |
Total: approximately 1.5 – 2.5 GB RAM.
Common commands
# Check if all services are running
docker compose ps
# View logs (all services)
docker compose logs -f
# View logs (backend only)
docker compose logs -f backend
# Stop QuoteNode
docker compose down
# Update to the latest version
docker compose pull && docker compose up -d
Keeping your secrets safe
Your .env file contains cryptographic secrets that are unique to your installation. Understanding what each secret protects helps you plan your backup strategy:
| Secret | What it protects | If lost |
|---|---|---|
DB_PASSWORD | Database access | Recoverable — reset via PostgreSQL admin tools |
DB_ENCRYPTION_KEY | MFA (TOTP) codes, SMTP password, FX API key — and all PII fields when ENCRYPT_PII=true | Permanently unreadable — these fields cannot be decrypted without the original key |
TIMING_TOKEN_SECRET | Anti-timing-attack tokens | Regenerate — existing tokens invalidated, users re-authenticate |
PUBLIC_LINK_PASSWORD_SESSION_SECRET | Public link sessions | Regenerate — active sessions expire, links still work |
The critical secret is
DB_ENCRYPTION_KEY. All other secrets can be regenerated with minor inconvenience. ButDB_ENCRYPTION_KEYencrypts data at rest — losing it means those fields are gone forever. IfENCRYPT_PII=true, this also includes all customer, contact, supplier, and user PII (names, emails, phones, tax IDs). Without PII encryption enabled, your business data is stored unencrypted and remains fully accessible.
For production systems, we recommend:
- Store a copy of
.envin a password manager (1Password, Bitwarden, etc.) - For maximum safety, print the secrets and keep the printout in a physical safe
- Never commit
.envto version control
Recovery if .env is lost
If you lose your .env file but have an unencrypted database backup (the default backup mode), you can restore your data:
- Create a fresh
.envwith new secrets - Restore the database from your backup (
pg_dump/pg_restore) - All business data (customers, offers, products, users) will be fully accessible
- Only a few fields are lost: MFA codes (users will need to re-enroll), SMTP password (re-enter in settings), FX API key (re-enter in settings). If
ENCRYPT_PII=truewas enabled, all PII data (customer names, emails, phones, tax IDs) is also lost — consider this carefully before choosing PII encryption
If your backups are GPG-encrypted (via BACKUP_GPG_RECIPIENT), you need the GPG private key to decrypt them first.
Production deployment
For production use, you need to:
- Generate proper secrets — never use the example values above in production:
# Generate a 64-character hex key for database encryption
openssl rand -hex 32
# Generate random secrets for tokens
openssl rand -base64 32
-
Set up a domain with HTTPS — configure your
DOMAINandCORS_ALLOWED_ORIGINSto your real domain. The frontend container (Caddy) automatically obtains Let’s Encrypt certificates. -
Configure email — add SMTP settings to your
.envto enable offer sending:
SMTP_HOST=smtp.yourprovider.com
SMTP_PORT=587
SMTP_USERNAME=[email protected]
SMTP_PASSWORD=your-smtp-password
SMTP_AUTH=true
SMTP_STARTTLS=true
-
Back up your
.envfile — store it securely before anything else. See Keeping your secrets safe above. -
Enable database backups — see the Backup & Recovery guide.
-
Review all settings — download the full .env.prod.example from the repository for a complete reference of all available configuration variables.
For advanced deployment options (Coolify PaaS, backup workers, GeoIP), see the Deployment Options wiki page.
Updating QuoteNode
To update to the latest version:
docker compose pull && docker compose up -d
Your
.envfile is never modified by updates. Docker pulls new container images but your configuration and data remain untouched. Do not delete or recreate your.envfile during updates — the same secrets must be used throughout the lifetime of your installation.
Database schema migrations run automatically on backend startup — no manual migration step is needed.
Troubleshooting
Backend won’t start
Check the logs: docker compose logs backend. Common issues:
- Database not ready — the backend waits for PostgreSQL to be healthy. If PostgreSQL is slow to start, the backend retries automatically.
- Invalid secrets —
DB_ENCRYPTION_KEYmust be exactly 64 hex characters.DB_PASSWORDmust match between the backend and PostgreSQL containers. - Port conflict — if port 80 or 443 is already in use, change the
portsmapping indocker-compose.yml.
Cannot connect to QuoteNode
- Verify all containers are running:
docker compose ps - Check that the frontend container shows “healthy”
- Try accessing
http://localhost(nothttps://for local testing without a real domain)
Forgot admin password
Stop the stack, delete the PostgreSQL volume, and restart — the admin account is recreated on first boot:
docker compose down -v # WARNING: deletes all data!
docker compose up -d
For production, use the password reset feature in the admin panel instead.
Next steps
- Configuration Guide — branding, email templates, security settings
- Deployment Options — Coolify, production topology, backup workers
- Backup & Recovery — automated backups with encryption