name: 'goatcounter' volumes: postgres-data: {} goatcounter-data: {} services: # PostgreSQL, suitable for running the tests or GoatCounter. postgres: image: 'postgres:18-alpine' ports: ['127.0.0.1:5432:5432'] volumes: ['postgres-data:/var/lib/postgresql'] shm_size: '1gb' healthcheck: {test: ['CMD-SHELL', 'pg_isready -U goatcounter -d goatcounter'], start_period: '30s', start_interval: '1s'} environment: 'POSTGRES_USER': 'goatcounter' 'POSTGRES_PASSWORD': 'goatcounter' 'POSTGRES_DATABASE': 'goatcounter' command: [ '-c', 'shared_preload_libraries=pg_stat_statements', '-c', 'default_statistics_target=1000', '-c', 'wal_level=minimal', # WAL '-c', 'max_wal_senders=0', '-c', 'min_wal_size=256MB', '-c', 'max_wal_size=4GB', '-c', 'shared_buffers=1GB', # Memory settings '-c', 'effective_cache_size=10GB', '-c', 'maintenance_work_mem=500MB', '-c', 'work_mem=200MB', '-c', 'max_parallel_workers_per_gather=4', # Run queries in parallel '-c', 'max_parallel_maintenance_workers=4', '-c', 'max_worker_processes=8', '-c', 'max_parallel_workers=8', '-c', 'random_page_cost=1.5', # Most people use an SSD, and random page access is faster than HDDs. '-c', 'effective_io_concurrency=256', # SSDs are better at concurrent access. '-c', 'jit=off', # Almost always slower. ] # Don't start GoatCounter by default, but only if asked for with: # docker compose up -d goatcounter-sqlite goatcounter-sqlite: profiles: ['goatcounter-sqlite'] build: {context: '.'} image: 'arp242/goatcounter:latest' ports: ['127.0.0.1:8080:8080'] volumes: ['goatcounter-data:/home/goatcounter/goatcounter-data'] # PostgreSQL version, start with: # docker compose up -d goatcounter-postgres goatcounter-postgres: profiles: ['goatcounter-postgres'] build: {context: '.'} image: 'arp242/goatcounter:latest' depends_on: ['postgres'] ports: ['127.0.0.1:8080:8080'] volumes: ['goatcounter-data:/home/goatcounter/goatcounter-data'] environment: 'GOATCOUNTER_DB': 'postgresql+postgresql://goatcounter:goatcounter@postgres:5432/goatcounter?sslmode=disable'