Self-Hosting Web Analytics with Docker and PostgreSQL

Melvin Prince
4 min read

One of the biggest advantages of open-source analytics is complete data ownership. When you self-host your analytics, no third party ever sees your traffic data. In this guide, we walk through deploying Xine Analytics on your own infrastructure using Docker Compose and PostgreSQL.

Why Self-Host?

Before we dive into the technical setup, here is why self-hosting your analytics matters:

  • Data sovereignty: Your analytics data stays on your server. No third-party processors, no data sharing agreements, no vendor lock-in.
  • Privacy compliance: Since data never leaves your infrastructure, GDPR Article 28 (data processor requirements) does not apply. You are both the controller and processor.
  • Cost efficiency: Xine is free and open-source. The only cost is your server, which can be as little as $5/month on a VPS.
  • Performance: A self-hosted analytics endpoint on your own domain avoids ad-blocker interference and reduces DNS lookups.

Prerequisites

You will need:

  • A Linux VPS (Ubuntu 22.04+ recommended) with at least 1GB RAM
  • Docker and Docker Compose installed
  • A domain name pointed to your server
  • Basic familiarity with the command line

Step 1: Clone the Repository

git clone https://github.com/unisource/xine.git
cd xine

Step 2: Configure Environment Variables

Create a .env file from the example:

cp .env.example .env

Edit the .env file with your configuration:

DB_HOST=postgres
DB_PORT=5432
DB_USER=xine_user
DB_PASSWORD=your_secure_password_here
DB_NAME=analytics_db
DASHBOARD_PASSWORD=your_dashboard_password

The DASHBOARD_PASSWORD is used for the single-user login to your analytics dashboard. Choose something strong—this protects access to all your analytics data.

Step 3: Launch with Docker Compose

docker-compose up -d

This starts two containers:

  1. PostgreSQL 16 — your analytics database with persistent data volume
  2. Xine App — the Next.js application serving both the dashboard and the data collection API

The first startup will automatically create the database schema using Drizzle ORM migrations.

Step 4: Set Up a Reverse Proxy

For production, you should put Xine behind Nginx with SSL. Here is a minimal Nginx configuration:

server {
    listen 443 ssl;
    server_name analytics.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/analytics.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/analytics.yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Use Certbot to obtain free SSL certificates from Let's Encrypt.

Step 5: Add the Tracking Script

On any website you want to track, add the Xine script tag:

<script defer data-domain="yourdomain.com" src="https://analytics.yourdomain.com/t.js"></script>

That is it. The 8KB tracking script will automatically start tracking pageviews, sessions, referrers, and browser data—without setting a single cookie.

Step 6: Access the Dashboard

Navigate to https://analytics.yourdomain.com and log in with the password you set in the .env file. You will see the full Xine dashboard with:

  • Realtime visitors — live view of active sessions
  • Traffic analytics — pageviews, sessions, bounce rate, and session duration
  • Acquisition sources — referrers, UTM campaigns, and search engines
  • Performance metrics — Core Web Vitals (LCP, FCP, CLS, INP, TTFB)
  • Session replays — visual recordings of user journeys

Database Maintenance

PostgreSQL handles analytical workloads extremely well, but for high-traffic sites (100K+ pageviews/month), consider:

  1. Partitioning the pageviews table by month for faster queries
  2. Setting up automated backups with pg_dump in a cron job
  3. Monitoring disk usage and setting up log rotation

Conclusion

Self-hosting your analytics gives you the ultimate combination of privacy, performance, and control. With Docker Compose, the entire setup takes under 15 minutes. Your data stays on your server, your visitors get a faster experience, and you maintain full GDPR compliance without a single cookie.

Ready to own your analytics? Get started with Xine →

All articles

Published by Melvin Prince at Unisource