Infrastructure Overview
Enclavr uses Docker Compose for deployment with 19 services across 3 isolated networks.
Architecture
The stack is organized into three Docker networks:
| Network | Purpose | Services |
frontend | Public-facing traffic | Frontend, Server, Coturn, Caddy |
backend | Internal service communication | Server, PostgreSQL, Redis, postgres-exporter, redis-exporter, postgres-backup |
monitoring | Metrics and observability | Prometheus, Grafana, Node Exporter, PostgreSQL Exporter, Redis Exporter, Loki, Alloy, Docker Socket Proxy, Coturn |
Services (19 total)
| # | Service | Image | Purpose | Profile |
| 1 | Frontend | Built from Dockerfile | Next.js web application (served via Nginx) | core |
| 2 | Server | Built from Dockerfile | Go backend API | core |
| 3 | PostgreSQL | postgres:18.3-alpine | Primary database | core |
| 4 | Redis | redis:8-alpine | Pub/Sub and caching | core |
| 5 | Coturn | coturn/coturn:4.9.0-alpine | TURN server for WebRTC NAT traversal | voice |
| 6 | Prometheus | prom/prometheus:v3.10.0-alpine | Metrics collection | monitoring |
| 7 | Grafana | grafana/grafana:11.2.0 | Metrics dashboards | monitoring |
| 8 | Node Exporter | prom/node-exporter:v1.8.0-alpine | System metrics | monitoring |
| 9 | PostgreSQL Exporter | prometheuscommunity/postgres-exporter:v0.15.0-alpine | Database metrics (custom queries) | backend, monitoring |
| 10 | Redis Exporter | oliver006/redis_exporter:v1.61.0-alpine | Cache metrics | monitoring |
| 11 | Loki | grafana/loki:3.6.8 | Log aggregation | monitoring |
| 12 | Alloy | grafana/alloy:v1.7.4 | Log collection agent (replaces Promtail) | monitoring |
| 13 | Alertmanager | prom/alertmanager:v0.28.1 | Alert routing and notification | monitoring |
| 14 | Docker Socket Proxy | tecnativa/docker-socket-proxy:0.1.2 | Secure Docker API access for Alloy | monitoring |
| 15 | MinIO | minio/minio:RELEASE.2025-03-12T18-04-18Z | S3-compatible object storage | storage |
| 16 | MinIO Client | minio/mc:RELEASE.2025-03-12T17-31-15Z | MinIO bucket initialization | storage |
| 17 | Postgres Backup | Custom Dockerfile | Automated database backups | backup |
| 18 | Caddy | caddy:2-alpine | Reverse proxy with automatic HTTPS | tls |
| 19 | Watchtower | containrrr/watchtower:latest | Automatic container updates | maintenance |
Docker Compose Profiles
Services are organized into profiles for flexible deployment:
| Profile | Services | Usage |
core (default) | PostgreSQL, Redis, Server, Frontend | make up |
monitoring | Prometheus, Grafana, Loki, Alloy, Alertmanager, Node Exporter, PostgreSQL Exporter, Redis Exporter, Docker Socket Proxy | make monitoring |
voice | Coturn | make voice |
storage | MinIO, MinIO Client | make storage |
backup | Postgres Backup | make backup |
tls | Caddy | make tls |
maintenance | Watchtower | docker compose --profile maintenance up -d |
full | All services (voice + monitoring + backup) | make full |
prod | All services + TLS | make prod |
Security
- Frontend serves on port 3000 directly
- Network isolation - 3 isolated networks (frontend, backend, monitoring); backend and monitoring have no internet egress
- Secrets - all credentials passed via environment variables, never in images
- Resource limits - every container has memory reservations and limits; server/coturn have CPU limits
- Health checks - all services include health checks for orchestration
- Non-root containers - all services run as non-root users with capability dropping
- Read-only filesystems - Redis, frontend, Prometheus, Loki, Alertmanager, Grafana, Alloy, Docker Socket Proxy, Watchtower, Caddy
- no-new-privileges - enabled on all services
- Docker Socket Proxy - Alloy never touches Docker socket directly
- Redis hardening - dangerous commands (FLUSHALL, FLUSHDB, DEBUG) disabled
Quick Start
cd infra
cp .env.example .env
docker compose up -d
After startup, access the application at http://localhost:3000.
PostgreSQL Initialization (init-db.sql)
The init-db.sql script runs on first database creation. It configures extensions, performance tuning, and monitoring.
Extensions
| Extension | Purpose |
uuid-ossp | UUID generation |
pg_trgm | Trigram similarity for text search |
pg_stat_statements | Query performance tracking |
pg_stat_statements Configuration
| Setting | Value |
shared_preload_libraries | pg_stat_statements |
pg_stat_statements.track | all |
pg_stat_statements.max | 5000 |
pg_stat_statements.track_utility | on |
Performance Tuning
| Setting | Value |
shared_buffers | 128MB |
effective_cache_size | 256MB |
work_mem | 4MB |
maintenance_work_mem | 64MB |
wal_buffers | 16MB |
checkpoint_completion_target | 0.9 |
max_wal_size | 1GB |
min_wal_size | 256MB |
max_connections | 100 |
random_page_cost | 1.1 |
effective_io_concurrency | 200 |
Logging
| Setting | Value |
log_min_duration_statement | 1000ms (log queries > 1s) |
log_checkpoints | on |
log_connections | on |
log_disconnections | on |
log_lock_waits | on |
log_temp_files | 0 (log all temp file usage) |
Autovacuum
| Setting | Value |
autovacuum_vacuum_scale_factor | 0.05 |
autovacuum_analyze_scale_factor | 0.02 |
autovacuum_max_workers | 2 |
Redis Configuration (redis.conf)
The redis.conf file configures Redis 8 for optimal pub/sub and caching performance.
Memory
| Setting | Value |
maxmemory | 200mb |
maxmemory-policy | allkeys-lru |
maxmemory-samples | 10 |
Persistence (AOF)
| Setting | Value |
appendonly | yes |
appendfsync | everysec |
auto-aof-rewrite-percentage | 100 |
auto-aof-rewrite-min-size | 64mb |
Persistence (RDB)
| Rule | Description |
save 60 1 | Save if 1+ keys changed in 60s |
save 300 100 | Save if 100+ keys changed in 300s |
save 600 1000 | Save if 1000+ keys changed in 600s |
Additional: stop-writes-on-bgsave-error: yes, rdbcompression: yes, rdbchecksum: yes
Performance
| Setting | Value |
tcp-backlog | 511 |
timeout | 300 (client idle timeout in seconds) |
tcp-keepalive | 60 |
lazyfree-lazy-eviction | yes |
lazyfree-lazy-expire | yes |
lazyfree-lazy-server-del | yes |
lazyfree-lazy-user-del | yes |
hz | 10 |
dynamic-hz | yes |
Security
| Setting | Value |
protected-mode | yes |
rename-command FLUSHALL | "" (disabled) |
rename-command FLUSHDB | "" (disabled) |
rename-command DEBUG | "" (disabled) |
maxclients | 1000 |
Ports
| Service | Port | Exposed |
| Frontend | 3000 | Yes |
| Server | 8080 | Yes |
| PostgreSQL | 5432 | No (internal) |
| Redis | 6379 | No (internal) |
| Coturn | 3478, 5349, 49152-49172 | Yes |
| Prometheus | 9090 | No (internal) |
| Grafana | 3030 | No (internal) |
Volumes
| Volume | Service | Purpose | Backup |
postgres_data | PostgreSQL | Database files | Yes |
redis_data | Redis | AOF/RDB persistence | No |
minio_data | MinIO | Object storage data | Yes |
grafana_data | Grafana | Dashboards and config | Yes |
prometheus_data | Prometheus | Metrics time-series | No |
server_uploads | Server | User-uploaded files | Yes |
loki_data | Loki | Log storage | No |
alertmanager_data | Alertmanager | Alert silences and history | No |
backup_data | Postgres Backup | Compressed database backups | Yes |
caddy_data | Caddy | TLS certificates | No |
caddy_config | Caddy | Caddy config cache | No |