Web Analytics Guide

    Deploying GoatCounter

    GoatCounter is an open-source, privacy-friendly web analytics platform that serves as a lightweight alternative to Google Analytics and Matomo. It tracks page views without collecting personal data, requires no GDPR consent notices, and adds only ~3.5 KB to your site. Deploy it on RamNode's reliable VPS hosting.

    Privacy-First
    Lightweight (~3.5 KB)
    No Cookies
    Open Source
    1

    Why GoatCounter?

    • Privacy-first: No cookies, no personal data tracking, no GDPR consent banners required
    • Lightweight: Single Go binary with ~3.5 KB tracking script — minimal impact on page load times
    • Simple dashboard: Clean, accessible interface focused on meaningful metrics over overwhelming data
    • Flexible integration: JavaScript snippet, no-JS tracking pixel, backend middleware, or log file import
    • Open source: EUPL-licensed, fully auditable, no vendor lock-in

    A RamNode 2 GB KVM VPS at $10/month provides more than enough headroom for even high-traffic sites. RamNode's 99.99% uptime SLA and premium network ensure your analytics remain consistently available.

    2

    Prerequisites

    RequirementDetails
    RamNode VPS2 GB KVM or higher (1 GB works for low-traffic sites)
    Operating SystemUbuntu 24.04 LTS (clean install recommended)
    Domain/Subdomaine.g., stats.yourdomain.com, pointed to your VPS IP via DNS A record
    SSH AccessRoot or sudo-capable user with SSH key authentication
    DNS PropagationEnsure DNS A record is active before starting

    GoatCounter requires a dedicated subdomain (e.g., stats.yourdomain.com). Configure your DNS A record to point to your RamNode VPS IP address before proceeding. DNS propagation can take up to 24–48 hours.

    3

    Initial Server Setup

    Connect and update system
    ssh root@YOUR_VPS_IP
    apt update && apt upgrade -y

    Create a Dedicated Service User

    Running GoatCounter as a dedicated non-root user follows security best practices and limits the blast radius of any potential vulnerability.

    Create service user
    useradd goatcounter --system --user-group \
      --shell /sbin/nologin \
      --comment "GoatCounter web analytics" \
      --create-home \
      --home-dir /var/lib/goatcounter

    Configure Firewall

    Set up UFW
    apt install ufw -y
    ufw allow OpenSSH
    ufw allow 80/tcp
    ufw allow 443/tcp
    ufw enable
    4

    Install GoatCounter

    GoatCounter distributes statically compiled binaries that include everything needed — no additional runtime dependencies required.

    Download and install the binary
    cd /var/lib/goatcounter
    wget https://github.com/arp242/goatcounter/releases/download/v2.7.0/goatcounter-v2.7.0-linux-amd64.gz
    gzip -d goatcounter-v2.7.0-linux-amd64.gz
    mv goatcounter-v2.7.0-linux-amd64 /usr/local/bin/goatcounter
    chmod +x /usr/local/bin/goatcounter
    Verify installation
    goatcounter version
    5

    Initialize the Database

    GoatCounter supports both SQLite and PostgreSQL. SQLite is the simplest option and performs well for most sites. For high-traffic deployments handling millions of daily page views, consider PostgreSQL.

    Option A: SQLite (Recommended)

    Create site with SQLite
    cd /var/lib/goatcounter
    sudo -u goatcounter /usr/local/bin/goatcounter db create site \
      -createdb \
      -user.email admin@yourdomain.com \
      -vhost stats.yourdomain.com

    You will be prompted to set a password for the dashboard login. This creates the SQLite database at /var/lib/goatcounter/goatcounter-data/db.sqlite3.

    Option B: PostgreSQL (High-Traffic Sites)

    Install and configure PostgreSQL
    apt install postgresql postgresql-contrib -y
    systemctl enable postgresql
    
    sudo -u postgres createuser --interactive --pwprompt goatcounter
    sudo -u postgres createdb --owner goatcounter goatcounter
    
    sudo -u goatcounter /usr/local/bin/goatcounter db create site \
      -db "postgresql+host=/run/postgresql dbname=goatcounter" \
      -user.email admin@yourdomain.com \
      -vhost stats.yourdomain.com
    6

    Configure the Systemd Service

    Create a systemd unit file so GoatCounter starts automatically on boot and restarts on failure.

    /etc/systemd/system/goatcounter.service
    [Unit]
    Description=GoatCounter web analytics
    After=network.target
    
    [Service]
    Type=simple
    Restart=always
    RestartSec=5
    WorkingDirectory=/var/lib/goatcounter
    ExecStart=/usr/local/bin/goatcounter serve \
      -listen :8091 \
      -tls none
    User=goatcounter
    Group=goatcounter
    CapabilityBoundingSet=
    PrivateTmp=true
    NoNewPrivileges=true
    PrivateDevices=true
    DevicePolicy=closed
    
    [Install]
    WantedBy=multi-user.target

    The -tls none flag tells GoatCounter not to handle TLS directly. We'll use Nginx as a reverse proxy with Let's Encrypt certificates instead.

    For PostgreSQL users, modify the ExecStart line to include your database connection:

    PostgreSQL ExecStart variant
    ExecStart=/usr/local/bin/goatcounter serve \
      -listen :8091 \
      -tls none \
      -db "postgresql+host=/run/postgresql dbname=goatcounter"
    Enable and start the service
    systemctl daemon-reload
    systemctl enable goatcounter
    systemctl start goatcounter
    systemctl status goatcounter

    Confirm the service shows "active (running)". If there are issues, check the logs:

    View logs
    journalctl -fu goatcounter
    7

    Nginx Reverse Proxy with SSL

    Install Nginx and Certbot
    apt install nginx certbot python3-certbot-nginx -y
    /etc/nginx/sites-available/goatcounter
    server {
        listen 80;
        server_name stats.yourdomain.com;
    
        location / {
            proxy_pass http://127.0.0.1:8091;
            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;
        }
    }
    Enable site and obtain SSL
    ln -s /etc/nginx/sites-available/goatcounter /etc/nginx/sites-enabled/
    nginx -t
    systemctl reload nginx
    
    # Obtain SSL certificate
    certbot --nginx -d stats.yourdomain.com
    Verify SSL auto-renewal
    certbot renew --dry-run
    8

    Add Tracking to Your Website

    Method 1: JavaScript Snippet (Recommended)

    Add the following script tag just before the closing </body> tag. The script is approximately 3.5 KB and loads asynchronously with negligible performance impact.

    JavaScript tracking snippet
    <script data-goatcounter="https://stats.yourdomain.com/count"
            async src="//stats.yourdomain.com/count.js"></script>

    Method 2: No-JavaScript Tracking Pixel

    For sites that need to work without JavaScript:

    Tracking pixel
    <noscript>
      <img src="https://stats.yourdomain.com/count?p=/page-path">
    </noscript>

    Method 3: Server-Side Log Import

    GoatCounter can parse access logs from Nginx, Apache, Caddy, CloudFront, and other servers:

    Import access logs
    goatcounter import -follow -format combined /var/log/nginx/access.log
    9

    Verify the Dashboard

    1. Open your browser and navigate to https://stats.yourdomain.com
    2. Log in with the email and password from the database initialization step
    3. Visit your website in a separate tab to generate test traffic
    4. Return to the GoatCounter dashboard — page views should appear within seconds

    The dashboard displays page views, referrers, browser and OS statistics, screen sizes, locations, and campaign tracking — all without collecting personally identifiable information.

    10

    Production Hardening

    11

    Alternative: Docker Deployment

    GoatCounter is also available on Docker Hub for containerized deployments.

    Quick Docker setup
    docker run -d \
      --name goatcounter \
      --restart unless-stopped \
      -p 8091:8080 \
      -v goatcounter-data:/home/goatcounter/goatcounter-data \
      arp242/goatcounter
    docker-compose.yml
    version: "3.8"
    services:
      goatcounter:
        image: arp242/goatcounter
        container_name: goatcounter
        restart: unless-stopped
        ports:
          - "8091:8080"
        volumes:
          - goatcounter-data:/home/goatcounter/goatcounter-data
    
    volumes:
      goatcounter-data:

    After starting the container, use the same Nginx reverse proxy configuration from Step 7. The named volume ensures your database persists across container restarts.

    12

    Troubleshooting

    IssueSolution
    Dashboard login refreshes without logging inEnsure your reverse proxy passes the X-Forwarded-Proto: https header so GoatCounter sets secure cookies correctly
    "zdb requires SQLite 3.35.0 or newer"Usually a file permission issue. Verify ownership: chown -R goatcounter:goatcounter /var/lib/goatcounter
    Service fails to start on port 80/443Use setcap or run behind a reverse proxy on a high port (8091) as shown in this guide
    No page views appearingVerify the tracking script URL matches your vhost domain exactly. Check browser dev tools for count.js loading errors
    High memory usage with SQLiteRun VACUUM periodically: sqlite3 /path/to/db.sqlite3 "VACUUM"
    Certificate renewal failsEnsure port 80 is open for ACME challenges. Check: journalctl -u certbot

    Quick Reference

    CommandDescription
    systemctl status goatcounterCheck service status
    systemctl restart goatcounterRestart the service
    journalctl -fu goatcounterView live logs
    goatcounter db migrate allRun pending migrations
    goatcounter db create siteCreate a new site entry
    goatcounter import -format combinedImport access logs
    goatcounter help serveView all serve options
    goatcounter versionCheck installed version

    GoatCounter Deployed Successfully!

    Your self-hosted, privacy-friendly analytics platform is now running. Add the tracking snippet to your websites and enjoy clean, cookie-free analytics.