How to Self-Host Dub Link Attribution Platform with Docker on Ubuntu 24.04 in 2025

How to Self-Host Dub Link Attribution Platform with Docker on Ubuntu 24.04 in 2025

Dub is an open-source link attribution platform that powers over 100M+ clicks monthly for companies like Twilio, Buffer, and Vercel. While Dub offers a hosted solution, self-hosting gives you complete control over your data, customization options, and cost management. This guide walks you through deploying Dub on Ubuntu 24.04 using Docker.

Why Self-Host Dub Instead of Using the Managed Service

Before diving into the setup, understand when self-hosting makes sense:

  • Data sovereignty: Keep all link tracking data on your infrastructure
  • Custom domain requirements: Full control over DNS and SSL configurations
  • Cost optimization: Eliminate per-click pricing for high-volume scenarios
  • Compliance needs: Meet specific regulatory requirements (GDPR, HIPAA)
  • Development environment: Test features before production deployment

Self-hosting requires maintaining your own infrastructure, but for teams processing millions of clicks or those with strict data policies, the tradeoff is worthwhile.

Prerequisites for Self-Hosting Dub

Before starting, ensure you have:

  • Ubuntu 24.04 LTS server (minimum 4GB RAM, 2 CPU cores)
  • Root or sudo access
  • Domain name with DNS access
  • Node.js v23.11.0 installed
  • pnpm 9.15.9 package manager
  • Docker and Docker Compose installed
  • Basic knowledge of environment variables and database connections

Step 1: Install Required Dependencies on Ubuntu 24.04

First, update your system and install Docker:

# Update package index
sudo apt update && sudo apt upgrade -y

# Install Docker prerequisites
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common

# Add Docker GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# Add Docker repository
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Install Docker Engine
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

# Add your user to docker group
sudo usermod -aG docker $USER
newgrp docker

Install Node.js v23.11.0 using nvm:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
source ~/.bashrc
nvm install 23.11.0
nvm use 23.11.0

# Install pnpm
npm install -g pnpm@9.15.9

Step 2: Clone and Configure the Dub Repository

Clone the official Dub repository:

cd /opt
sudo git clone https://github.com/dubinc/dub.git
sudo chown -R $USER:$USER dub
cd dub

Create your environment configuration file:

cp .env.example .env

Edit the .env file with your specific configuration. Here are the critical variables:

# Database Configuration
DATABASE_URL="mysql://user:password@localhost:3306/dub"

# Redis Configuration (Upstash or self-hosted)
UPSTASH_REDIS_REST_URL="your-redis-url"
UPSTASH_REDIS_REST_TOKEN="your-redis-token"

# NextAuth Configuration
NEXTAUTH_SECRET="generate-with-openssl-rand-base64-32"
NEXTAUTH_URL="https://yourdomain.com"

# Tinybird Analytics (optional for analytics)
TINYBIRD_API_KEY="your-tinybird-key"

# Email Configuration (Resend)
RESEND_API_KEY="your-resend-key"

# Stripe (if using payments)
STRIPE_SECRET_KEY="your-stripe-key"
STRIPE_WEBHOOK_SECRET="your-webhook-secret"

Generate a secure NextAuth secret:

openssl rand -base64 32

Step 3: Set Up Supporting Services

Dub's tech stack requires several services. Create a docker-compose.yml for the core infrastructure:

version: '3.8'
services:
  mysql:
    image: mysql:8.0
    container_name: dub-mysql
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: dub
      MYSQL_USER: dubuser
      MYSQL_PASSWORD: dubpassword
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    container_name: dub-redis
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    restart: unless-stopped

volumes:
  mysql-data:
  redis-data:

Start the services:

docker compose up -d

Step 4: Database Setup and Prisma Migration

With MySQL running, initialize the database schema:

cd apps/web
pnpm install
pnpm prisma:push

This command pushes the Prisma schema to your database without creating migration files. If you encounter the error "The table <table-name> does not exist in the current database", this step resolves it.

Seed the database with development data:

pnpm run script dev/seed

For a clean seed (deleting existing data):

pnpm run script dev/seed --truncate

Step 5: Build and Deploy Dub Application

Return to the root directory and build the project:

cd /opt/dub
pnpm install
pnpm build

Common build errors and fixes:

  • Build fails with TypeScript errors: Verify Node.js version is exactly v23.11.0
  • Module not found errors: Delete all node_modules, .next, and .turbo directories:
find . -name "node_modules" -type d -prune -exec rm -rf '{}' +
find . -name ".next" -type d -prune -exec rm -rf '{}' +
find . -name ".turbo" -type d -prune -exec rm -rf '{}' +
pnpm install
pnpm build

Step 6: Configure Nginx Reverse Proxy

Install and configure Nginx:

sudo apt install -y nginx certbot python3-certbot-nginx

Create an Nginx configuration:

sudo nano /etc/nginx/sites-available/dub

Add this configuration:

server {
    listen 80;
    server_name yourdomain.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        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 the site:

sudo ln -s /etc/nginx/sites-available/dub /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx

Obtain SSL certificate:

sudo certbot --nginx -d yourdomain.com

Step 7: Create Systemd Service for Production

Create a systemd service to manage Dub:

sudo nano /etc/systemd/system/dub.service

Add this configuration:

[Unit]
Description=Dub Link Attribution Platform
After=network.target mysql.service redis.service

[Service]
Type=simple
User=your-username
WorkingDirectory=/opt/dub/apps/web
Environment=NODE_ENV=production
ExecStart=/home/your-username/.nvm/versions/node/v23.11.0/bin/pnpm start
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target

Enable and start the service:

sudo systemctl daemon-reload
sudo systemctl enable dub
sudo systemctl start dub
sudo systemctl status dub

Comparing Self-Hosted vs Managed Dub Deployment

| Aspect | Self-Hosted | Managed (Vercel) | |--------|-------------|------------------| | Setup Time | 2-4 hours | 5 minutes | | Monthly Cost | $20-100 (VPS) | Usage-based | | Maintenance | Manual updates | Automatic | | Data Control | Full ownership | Third-party | | Scalability | Manual scaling | Auto-scaling | | Customization | Unlimited | Limited | | SSL/DNS | Self-managed | Automatic |

Performance Optimization Tips

  1. Enable Redis caching: Significantly reduces database queries
  2. Configure CDN: Use Cloudflare for static assets
  3. Database indexing: Monitor slow queries with EXPLAIN
  4. Resource monitoring: Install htop and monitor memory usage
  5. Log rotation: Configure logrotate to prevent disk space issues

Troubleshooting Common Issues

Port 3000 already in use:

sudo lsof -i :3000
sudo kill -9 <PID>

Database connection failures:

  • Verify MySQL is running: docker ps
  • Check credentials in .env match docker-compose
  • Ensure DATABASE_URL format is correct

Prisma schema sync errors:

cd apps/web
pnpm prisma generate
pnpm prisma:push

Monitoring and Maintenance

Set up basic monitoring:

# Check application logs
journalctl -u dub -f

# Monitor Docker containers
docker stats

# Database backup script
mysqldump -u dubuser -p dub > backup-$(date +%Y%m%d).sql

Schedule weekly backups with cron:

crontab -e
# Add: 0 2 * * 0 /opt/dub/backup.sh

Next Steps After Deployment

Once Dub is running:

  1. Configure your first workspace and custom domain
  2. Set up conversion tracking with the Dub TypeScript SDK
  3. Integrate with your marketing automation tools
  4. Configure team access and SSO if needed (BoxyHQ integration)
  5. Set up analytics dashboards with Tinybird

For production workloads, consider deploying on managed platforms like DigitalOcean droplets with automated backups, Render for simplified container deployments, or Supabase for the database layer with built-in replication.

Self-hosting Dub gives you the foundation for a scalable link attribution system under your complete control. The open-source nature means you can customize every aspect while benefiting from the same platform used by industry-leading teams.

Recommended Tools