How to Self-Host Shynet with Docker Compose

What Is Shynet?

Shynet is a privacy-focused, cookie-free web analytics platform that tracks page views and sessions without JavaScript (it uses a tracking pixel by default, with an optional JS snippet for richer data). It replaces Google Analytics for users who want accurate visitor counts without compromising privacy. Built with Django and open-source under the Apache 2.0 license, Shynet provides a clean dashboard showing page views, sessions, referrers, and device information — all without cookies or fingerprinting.

Prerequisites

  • A Linux server (Ubuntu 22.04+ recommended)
  • Docker and Docker Compose installed (guide)
  • 512 MB of free RAM (minimum)
  • 2 GB of free disk space
  • A domain name (optional, for remote access)

Docker Compose Configuration

Create a directory for your Shynet deployment:

mkdir -p ~/shynet && cd ~/shynet

Create a docker-compose.yml file:

services:
  shynet:
    image: milesmcc/shynet:v0.13.1
    container_name: shynet
    depends_on:
      db:
        condition: service_healthy
    environment:
      # Database connection
      DB_NAME: shynet
      DB_USER: shynet
      DB_PASSWORD: ${DB_PASSWORD}
      DB_HOST: db
      DB_PORT: "5432"
      # Django settings — CHANGE the secret key
      DJANGO_SECRET_KEY: ${DJANGO_SECRET_KEY}
      # Your domain (no trailing slash)
      ALLOWED_HOSTS: ${SHYNET_HOST:-*}
      CSRF_TRUSTED_ORIGINS: ${CSRF_ORIGINS:-https://analytics.example.com}
      # Application settings
      PORT: "8080"
      PERFORM_CHECKS_AND_SETUP: "True"
      SCRIPT_USE_HTTPS: "True"
      ACCOUNT_SIGNUPS_ENABLED: "False"
      TIME_ZONE: "UTC"
    ports:
      - "8080:8080"
    restart: unless-stopped
    networks:
      - shynet-net

  db:
    image: postgres:16-alpine
    container_name: shynet-db
    environment:
      POSTGRES_DB: shynet
      POSTGRES_USER: shynet
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - shynet_db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U shynet"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped
    networks:
      - shynet-net

volumes:
  shynet_db:

networks:
  shynet-net:
    driver: bridge

Create a .env file alongside it:

# Database password — CHANGE THIS to a strong random value
DB_PASSWORD=change-me-to-a-strong-random-password

# Django secret key — generate with: python3 -c "import secrets; print(secrets.token_urlsafe(50))"
DJANGO_SECRET_KEY=change-me-to-a-long-random-string

# Your Shynet domain (used for ALLOWED_HOSTS and CSRF)
SHYNET_HOST=analytics.example.com
CSRF_ORIGINS=https://analytics.example.com

Start the stack:

docker compose up -d

Initial Setup

After the containers start, create your admin account:

docker exec -it shynet ./manage.py registeradmin [email protected]

You’ll be prompted to set a password. Then set the instance name:

docker exec -it shynet ./manage.py whitelabel "My Analytics"

Access the web UI at http://your-server-ip:8080 and log in with the email and password you just created.

To add a site for tracking:

  1. Click “Create a Service” in the dashboard
  2. Enter your site name and URL
  3. Copy the tracking pixel (<img> tag) or JavaScript snippet
  4. Add it to your website’s HTML
Tracking MethodProsCons
Tracking pixel (<img>)No JavaScript needed, works everywhereNo session duration or page load times
JavaScript snippetSession data, page load metrics, SPA supportRequires JS execution

Configuration

Email Notifications (Optional)

Add SMTP settings to receive alerts about your analytics:

environment:
  EMAIL_HOST: smtp.example.com
  EMAIL_PORT: "587"
  EMAIL_HOST_USER: [email protected]
  EMAIL_HOST_PASSWORD: ${SMTP_PASSWORD}
  EMAIL_USE_TLS: "True"
  SERVER_EMAIL: [email protected]

Multi-Instance Scaling with Redis

For high-traffic sites, add Redis for caching and Celery task processing:

services:
  redis:
    image: redis:7-alpine
    container_name: shynet-redis
    restart: unless-stopped
    networks:
      - shynet-net

  shynet:
    environment:
      REDIS_CACHE_LOCATION: redis://redis:6379/0
      CELERY_BROKER_URL: redis://redis:6379/1
      CELERY_TASK_ALWAYS_EAGER: "False"

Key Environment Variables

VariableDefaultDescription
ACCOUNT_SIGNUPS_ENABLEDFalseAllow public account registration
SCRIPT_USE_HTTPSTrueUse HTTPS in tracking scripts
PERFORM_CHECKS_AND_SETUPTrueAuto-run migrations on startup
SHOW_SHYNET_VERSIONTrueDisplay version in footer
TIME_ZONEUTCDashboard timezone

Reverse Proxy

Place Shynet behind a reverse proxy for SSL termination. With Nginx Proxy Manager:

  • Scheme: http
  • Forward Hostname: shynet (container name) or your server IP
  • Forward Port: 8080
  • Enable SSL and force HTTPS

Set CSRF_TRUSTED_ORIGINS to your public URL (e.g., https://analytics.example.com). See our Reverse Proxy Setup guide for detailed configuration.

Backup

Back up the PostgreSQL database regularly:

docker exec shynet-db pg_dump -U shynet shynet > shynet_backup_$(date +%Y%m%d).sql

The shynet_db volume contains all persistent data. Include it in your backup strategy.

Troubleshooting

CSRF Verification Failed

Symptom: 403 Forbidden error when logging in. Fix: Ensure CSRF_TRUSTED_ORIGINS matches your exact public URL including the protocol (https://analytics.example.com). Restart the container after changing it.

Tracking Pixel Not Recording Visits

Symptom: Dashboard shows zero hits despite pixel placement. Fix: Verify the pixel URL is reachable from the client browser. Check that ALLOWED_HOSTS includes your domain. If using HTTPS, set SCRIPT_USE_HTTPS=True.

Database Connection Refused

Symptom: Shynet fails to start with “could not connect to server” errors. Fix: Ensure the db container is healthy (docker compose ps). The depends_on with health check should handle startup order, but PostgreSQL may need more time on first run.

Admin Account Locked Out

Symptom: Forgot admin password. Fix: Reset via Django management command:

docker exec -it shynet ./manage.py changepassword [email protected]

Resource Requirements

  • RAM: ~150 MB idle, ~300 MB under moderate load
  • CPU: Low — Django handles analytics ingestion efficiently
  • Disk: ~500 MB for application, plus database growth (~1 GB per million page views)

Verdict

Shynet is an excellent choice if you want dead-simple, privacy-respecting analytics without the complexity of Matomo or the JavaScript dependency of Plausible. The tracking pixel approach means it works on sites where JavaScript is blocked or unwanted. The trade-off is fewer features — no funnel analysis, no real-time dashboards, no custom events. For most personal sites and small projects, that’s a fair trade. If you need product analytics or advanced segmentation, look at PostHog instead.

Comments