Marketing Dashboard
Self-hosted marketing operations dashboard for product-led growth, checkout analytics, paid media, SEO signals, and Stripe readiness.
Configuration
services:
marketing-dashboard:
image: ghcr.io/abel-yelin/marketing-dashboard:latest
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
environment:
DATABASE_URL: ${DATABASE_URL}
DIRECT_URL: ${DIRECT_URL}
MARKETING_APP_URL: ${MARKETING_APP_URL}
MARKETING_COLLECTOR_UPSTREAM_URL: ${MARKETING_COLLECTOR_UPSTREAM_URL}
MARKETING_SESSION_TIMEOUT_MINUTES: ${MARKETING_SESSION_TIMEOUT_MINUTES}
MARKETING_PROXY_TIMEOUT_MS: ${MARKETING_PROXY_TIMEOUT_MS}
MARKETING_INGEST_MAX_BODY_BYTES: ${MARKETING_INGEST_MAX_BODY_BYTES}
MARKETING_INGEST_IP_RATE_LIMIT: ${MARKETING_INGEST_IP_RATE_LIMIT}
MARKETING_INGEST_CLIENT_RATE_LIMIT: ${MARKETING_INGEST_CLIENT_RATE_LIMIT}
MARKETING_INGEST_RATE_LIMIT_WINDOW_MS: ${MARKETING_INGEST_RATE_LIMIT_WINDOW_MS}
MARKETING_ENABLE_PILOT_API: ${MARKETING_ENABLE_PILOT_API}
MARKETING_DASHBOARD_ADMIN_USER: ${MARKETING_DASHBOARD_ADMIN_USER}
MARKETING_DASHBOARD_ADMIN_PASSWORD: ${MARKETING_DASHBOARD_ADMIN_PASSWORD}
ports:
- 3000
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://localhost:3000/api/health > /dev/null || exit 1"]
interval: 15s
timeout: 5s
retries: 10
start_period: 30s
postgres:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- marketing-dashboard-postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U \"$${POSTGRES_USER}\" -d \"$${POSTGRES_DB}\""]
interval: 10s
timeout: 5s
retries: 10
volumes:
marketing-dashboard-postgres:[variables]
main_domain = "${domain}"
postgres_password = "${password:32}"
admin_password = "${password:32}"
[config]
mounts = []
[[config.domains]]
serviceName = "marketing-dashboard"
port = 3000
host = "${main_domain}"
[config.env]
POSTGRES_DB = "marketing_dashboard"
POSTGRES_USER = "marketing_dashboard"
POSTGRES_PASSWORD = "${postgres_password}"
DATABASE_URL = "postgresql://marketing_dashboard:${postgres_password}@postgres:5432/marketing_dashboard?schema=public"
DIRECT_URL = "postgresql://marketing_dashboard:${postgres_password}@postgres:5432/marketing_dashboard?schema=public"
MARKETING_APP_URL = "https://${main_domain}"
MARKETING_COLLECTOR_UPSTREAM_URL = "https://${main_domain}/api/ingest/browser"
MARKETING_SESSION_TIMEOUT_MINUTES = "30"
MARKETING_PROXY_TIMEOUT_MS = "4000"
MARKETING_INGEST_MAX_BODY_BYTES = "65536"
MARKETING_INGEST_IP_RATE_LIMIT = "300"
MARKETING_INGEST_CLIENT_RATE_LIMIT = "1200"
MARKETING_INGEST_RATE_LIMIT_WINDOW_MS = "60000"
MARKETING_ENABLE_PILOT_API = "false"
MARKETING_DASHBOARD_ADMIN_USER = "admin"
MARKETING_DASHBOARD_ADMIN_PASSWORD = "${admin_password}"Base64
To import this template in Dokploy: create a Compose service → Advanced → Base64 import and paste the content below:
ewogICJjb21wb3NlIjogInNlcnZpY2VzOlxuICBtYXJrZXRpbmctZGFzaGJvYXJkOlxuICAgIGltYWdlOiBnaGNyLmlvL2FiZWwteWVsaW4vbWFya2V0aW5nLWRhc2hib2FyZDpsYXRlc3RcbiAgICByZXN0YXJ0OiB1bmxlc3Mtc3RvcHBlZFxuICAgIGRlcGVuZHNfb246XG4gICAgICBwb3N0Z3JlczpcbiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHlcbiAgICBlbnZpcm9ubWVudDpcbiAgICAgIERBVEFCQVNFX1VSTDogJHtEQVRBQkFTRV9VUkx9XG4gICAgICBESVJFQ1RfVVJMOiAke0RJUkVDVF9VUkx9XG4gICAgICBNQVJLRVRJTkdfQVBQX1VSTDogJHtNQVJLRVRJTkdfQVBQX1VSTH1cbiAgICAgIE1BUktFVElOR19DT0xMRUNUT1JfVVBTVFJFQU1fVVJMOiAke01BUktFVElOR19DT0xMRUNUT1JfVVBTVFJFQU1fVVJMfVxuICAgICAgTUFSS0VUSU5HX1NFU1NJT05fVElNRU9VVF9NSU5VVEVTOiAke01BUktFVElOR19TRVNTSU9OX1RJTUVPVVRfTUlOVVRFU31cbiAgICAgIE1BUktFVElOR19QUk9YWV9USU1FT1VUX01TOiAke01BUktFVElOR19QUk9YWV9USU1FT1VUX01TfVxuICAgICAgTUFSS0VUSU5HX0lOR0VTVF9NQVhfQk9EWV9CWVRFUzogJHtNQVJLRVRJTkdfSU5HRVNUX01BWF9CT0RZX0JZVEVTfVxuICAgICAgTUFSS0VUSU5HX0lOR0VTVF9JUF9SQVRFX0xJTUlUOiAke01BUktFVElOR19JTkdFU1RfSVBfUkFURV9MSU1JVH1cbiAgICAgIE1BUktFVElOR19JTkdFU1RfQ0xJRU5UX1JBVEVfTElNSVQ6ICR7TUFSS0VUSU5HX0lOR0VTVF9DTElFTlRfUkFURV9MSU1JVH1cbiAgICAgIE1BUktFVElOR19JTkdFU1RfUkFURV9MSU1JVF9XSU5ET1dfTVM6ICR7TUFSS0VUSU5HX0lOR0VTVF9SQVRFX0xJTUlUX1dJTkRPV19NU31cbiAgICAgIE1BUktFVElOR19FTkFCTEVfUElMT1RfQVBJOiAke01BUktFVElOR19FTkFCTEVfUElMT1RfQVBJfVxuICAgICAgTUFSS0VUSU5HX0RBU0hCT0FSRF9BRE1JTl9VU0VSOiAke01BUktFVElOR19EQVNIQk9BUkRfQURNSU5fVVNFUn1cbiAgICAgIE1BUktFVElOR19EQVNIQk9BUkRfQURNSU5fUEFTU1dPUkQ6ICR7TUFSS0VUSU5HX0RBU0hCT0FSRF9BRE1JTl9QQVNTV09SRH1cbiAgICBwb3J0czpcbiAgICAgIC0gMzAwMFxuICAgIGhlYWx0aGNoZWNrOlxuICAgICAgdGVzdDogW1wiQ01ELVNIRUxMXCIsIFwiY3VybCAtZnNTIGh0dHA6Ly9sb2NhbGhvc3Q6MzAwMC9hcGkvaGVhbHRoID4gL2Rldi9udWxsIHx8IGV4aXQgMVwiXVxuICAgICAgaW50ZXJ2YWw6IDE1c1xuICAgICAgdGltZW91dDogNXNcbiAgICAgIHJldHJpZXM6IDEwXG4gICAgICBzdGFydF9wZXJpb2Q6IDMwc1xuXG4gIHBvc3RncmVzOlxuICAgIGltYWdlOiBwb3N0Z3JlczoxNlxuICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkXG4gICAgZW52aXJvbm1lbnQ6XG4gICAgICBQT1NUR1JFU19EQjogJHtQT1NUR1JFU19EQn1cbiAgICAgIFBPU1RHUkVTX1VTRVI6ICR7UE9TVEdSRVNfVVNFUn1cbiAgICAgIFBPU1RHUkVTX1BBU1NXT1JEOiAke1BPU1RHUkVTX1BBU1NXT1JEfVxuICAgIHZvbHVtZXM6XG4gICAgICAtIG1hcmtldGluZy1kYXNoYm9hcmQtcG9zdGdyZXM6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhXG4gICAgaGVhbHRoY2hlY2s6XG4gICAgICB0ZXN0OiBbXCJDTUQtU0hFTExcIiwgXCJwZ19pc3JlYWR5IC1VIFxcXCIkJHtQT1NUR1JFU19VU0VSfVxcXCIgLWQgXFxcIiQke1BPU1RHUkVTX0RCfVxcXCJcIl1cbiAgICAgIGludGVydmFsOiAxMHNcbiAgICAgIHRpbWVvdXQ6IDVzXG4gICAgICByZXRyaWVzOiAxMFxuXG52b2x1bWVzOlxuICBtYXJrZXRpbmctZGFzaGJvYXJkLXBvc3RncmVzOlxuIiwKICAiY29uZmlnIjogIlt2YXJpYWJsZXNdXG5tYWluX2RvbWFpbiA9IFwiJHtkb21haW59XCJcbnBvc3RncmVzX3Bhc3N3b3JkID0gXCIke3Bhc3N3b3JkOjMyfVwiXG5hZG1pbl9wYXNzd29yZCA9IFwiJHtwYXNzd29yZDozMn1cIlxuXG5bY29uZmlnXVxubW91bnRzID0gW11cblxuW1tjb25maWcuZG9tYWluc11dXG5zZXJ2aWNlTmFtZSA9IFwibWFya2V0aW5nLWRhc2hib2FyZFwiXG5wb3J0ID0gMzAwMFxuaG9zdCA9IFwiJHttYWluX2RvbWFpbn1cIlxuXG5bY29uZmlnLmVudl1cblBPU1RHUkVTX0RCID0gXCJtYXJrZXRpbmdfZGFzaGJvYXJkXCJcblBPU1RHUkVTX1VTRVIgPSBcIm1hcmtldGluZ19kYXNoYm9hcmRcIlxuUE9TVEdSRVNfUEFTU1dPUkQgPSBcIiR7cG9zdGdyZXNfcGFzc3dvcmR9XCJcbkRBVEFCQVNFX1VSTCA9IFwicG9zdGdyZXNxbDovL21hcmtldGluZ19kYXNoYm9hcmQ6JHtwb3N0Z3Jlc19wYXNzd29yZH1AcG9zdGdyZXM6NTQzMi9tYXJrZXRpbmdfZGFzaGJvYXJkP3NjaGVtYT1wdWJsaWNcIlxuRElSRUNUX1VSTCA9IFwicG9zdGdyZXNxbDovL21hcmtldGluZ19kYXNoYm9hcmQ6JHtwb3N0Z3Jlc19wYXNzd29yZH1AcG9zdGdyZXM6NTQzMi9tYXJrZXRpbmdfZGFzaGJvYXJkP3NjaGVtYT1wdWJsaWNcIlxuTUFSS0VUSU5HX0FQUF9VUkwgPSBcImh0dHBzOi8vJHttYWluX2RvbWFpbn1cIlxuTUFSS0VUSU5HX0NPTExFQ1RPUl9VUFNUUkVBTV9VUkwgPSBcImh0dHBzOi8vJHttYWluX2RvbWFpbn0vYXBpL2luZ2VzdC9icm93c2VyXCJcbk1BUktFVElOR19TRVNTSU9OX1RJTUVPVVRfTUlOVVRFUyA9IFwiMzBcIlxuTUFSS0VUSU5HX1BST1hZX1RJTUVPVVRfTVMgPSBcIjQwMDBcIlxuTUFSS0VUSU5HX0lOR0VTVF9NQVhfQk9EWV9CWVRFUyA9IFwiNjU1MzZcIlxuTUFSS0VUSU5HX0lOR0VTVF9JUF9SQVRFX0xJTUlUID0gXCIzMDBcIlxuTUFSS0VUSU5HX0lOR0VTVF9DTElFTlRfUkFURV9MSU1JVCA9IFwiMTIwMFwiXG5NQVJLRVRJTkdfSU5HRVNUX1JBVEVfTElNSVRfV0lORE9XX01TID0gXCI2MDAwMFwiXG5NQVJLRVRJTkdfRU5BQkxFX1BJTE9UX0FQSSA9IFwiZmFsc2VcIlxuTUFSS0VUSU5HX0RBU0hCT0FSRF9BRE1JTl9VU0VSID0gXCJhZG1pblwiXG5NQVJLRVRJTkdfREFTSEJPQVJEX0FETUlOX1BBU1NXT1JEID0gXCIke2FkbWluX3Bhc3N3b3JkfVwiXG4iCn0=Links
Tags
analytics, marketing, self-hosted, postgresql, nextjs
Version: latest
Mailu
Full-featured open-source mail server as a set of Docker containers: SMTP (Postfix), IMAP (Dovecot), Rspamd antispam, admin UI with DKIM management and Roundcube webmail.
Markup
Markup is a fast, keyboard-first markdown workspace with live preview, graph view, whiteboards, mind maps, and PostgreSQL-powered cloud sync.