Installation
Install on a Windows LAN Server
Run QuoteNode with Docker Desktop on a Windows PC inside an office network.
Install on a Windows LAN Server
Use this path when a Windows PC in the office should host QuoteNode for users on the same local network.
When to use it
- The sales team works inside one office LAN.
- Customers do not need to open offer links from the public internet.
- You want an internal beta without managing a public VPS.
What will not work
- Public offer links and notification links will only work for devices on the same LAN or VPN. External customers cannot open a private LAN address.
- Email delivery can work with SMTP, but links inside emails still point to the LAN URL.
- Windows sleep, reboots, or Docker Desktop updates can interrupt service.
Prerequisites
- Docker Desktop with Compose v2 enabled.
- A stable LAN IP address or DHCP reservation for the Windows host.
- At least 2 GB RAM available and 10 GB free disk space.
- Regular backup location outside the Docker volume.
Verify your setup in PowerShell or Command Prompt:
docker --version # Should show 24.0+
docker compose version # Should show v2.x
Step 1 — Create a project directory
mkdir quotenode
cd quotenode
Step 2 — Create the configuration file
Create a file named .env in the project directory. Replace 192.168.1.50 with the actual IP of the Windows host (or an internal DNS name like quotenode.local). 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, e.g. in Git Bash or WSL? See Deployment environment variables.)
# 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
# LAN URL — replace with your PC's IP or internal DNS name
CORS_ALLOWED_ORIGINS=http://192.168.1.50
DOMAIN=192.168.1.50
# 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
Save your
.envfile immediately. Store a copy outside this PC. If you loseDB_ENCRYPTION_KEY, encrypted fields become permanently unreadable.
Step 3 — Create the Docker Compose file
Create a file named docker-compose.yml:
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:
Step 4 — Start QuoteNode
docker compose up -d
Docker will download the container images on the first run. Watch the startup:
docker compose logs -f backend
Wait until you see Started QuoteNodeApplication.
Step 5 — Open QuoteNode
Navigate to http://192.168.1.50 (or your PC address) from any device on the LAN.
Default login credentials:
| Field | Value |
|---|---|
[email protected] | |
| Password | Admin123! |
Change the default password immediately after logging in.
Windows firewall
- Allow inbound TCP on port 80 for HTTP access from the LAN.
- Allow inbound TCP on port 443 only if you configure HTTPS.
- Do not expose PostgreSQL to the LAN unless you have a specific admin need.
- Keep Docker Desktop running under an account and startup policy appropriate for your office.
What is running?
| 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 service status
docker compose ps
# View logs
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
| Secret | What it protects | If lost |
|---|---|---|
DB_PASSWORD | Database access | Recoverable — reset via PostgreSQL admin tools |
DB_ENCRYPTION_KEY | MFA codes, SMTP password, FX API key, PII fields | Permanently unreadable |
TIMING_TOKEN_SECRET | Anti-timing-attack tokens | Regenerate — users re-authenticate |
PUBLIC_LINK_PASSWORD_SESSION_SECRET | Public link sessions | Regenerate — active sessions expire |
The critical secret is
DB_ENCRYPTION_KEY. Store.envbackups outside this PC. See Installation troubleshooting for recovery steps.
Recommended next steps
- Configure backups and test a restore.
- Use Installation troubleshooting for CORS or link problems.
- Move to Ubuntu public server or Coolify before sending public links to external customers.