
Introduction
Dub’s codebase is set up in a monorepo (via Turborepo) and is fully open-source on GitHub. Here’s the monorepo structure:apps directory contains the code for:
web: The entirety of Dub’s application (app.dub.co) + our link redirect infrastructure.
packages directory contains the code for:
cli: A CLI for easily shortening URLs with the Dub API.email: Dub’s email application with function to send emails and templates.embeds: A package used embed Dub’s referral dashboard.prisma: Prisma Configuration for Dub’s web-app.stripe-app: The Stripe app for dub conversions.tailwind-config: The Tailwind CSS configuration for Dub’s web app.tinybird: Dub’s Tinybird configuration.tsconfig: The TypeScript configuration for Dub’s web app.ui: Dub’s UI component library.utils: A collection of utility functions and constants used across Dub’s codebase.
How app.dub.co works
Dub’s web app is built with Next.js and TailwindCSS.
It also utilizes code from the packages directory, specifically the @dub/ui and @dub/utils packages.
All of the code for the web app is located in here: main/apps/web/app/app.dub.co. This is using the Next.js route group pattern.
There’s also the API server, which is located in here: main/apps/web/app/api
When you run pnpm dev to start the development server, the app will be available at http://localhost:8888. The reason we use localhost:8888 and not app.localhost:8888 is because Google OAuth doesn’t allow you to use localhost subdomains.
How link redirects work on Dub
Link redirects on Dub are powered by Next.js Middleware. To handle high traffic, we use Redis to cache every link’s metadata when it’s first created. This allows us to serve redirects without hitting our MySQL database. Here’s the code that powers link redirects:main/apps/web/lib/middleware/link.ts
Running Dub locally
To run Dub locally, you’ll need to set up the following:- A Tinybird account
- An Upstash account
- A PlanetScale-compatible MySQL database
Step 1: Local setup
First, you’ll need to clone the Dub repo and install the dependencies.Clone the repo
Install dependencies
Build internal packages
Set up environment variables
.env.example file from ./apps/web to .env by executing the following command from apps/web:.env file with your own values as you progress through the setup.Step 2: Set up Tinybird Clickhouse database
Next, you’ll need to set up the Tinybird Clickhouse database. This will be used to store time-series click events data.Create Tinybird Workspace
us-east-1 region.Copy your admin Auth Token. Paste this token as the TINYBIRD_API_KEY environment variable in your .env file.Install Tinybird CLI and authenticate
packages/tinybird directory.If you have brew, install pipx by running brew install pipx. If not, you can check installation guide for other options. After that, install the Tinybird CLI with pipx install tinybird-cli (requires Python >= 3.8).Run tb auth --interactive and paste your admin Auth Token.Publish Tinybird datasource and endpoints
tb deploy to publish the datasource and endpoints in the packages/tinybird directory. You should see the following output (truncated for brevity):Set up Tinybird API base URL
TINYBIRD_API_URL environment variable in your .env file.Step 3: Set up Upstash Redis database
Next, you’ll need to set up the Upstash Redis database. This will be used to cache link metadata and serve link redirects.Create Upstash database

Set up Upstash Redis environment variables
UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN from the REST API section into your .env file.
QSTASH_TOKEN, QSTASH_CURRENT_SIGNING_KEY, and QSTASH_NEXT_SIGNING_KEY from the Request Builder section into your .env file.
Optional: Set up Ngrok tunnel
ngrok, and then run the following command to start an Ngrok tunnel at port 8888:https URL and paste it as the NEXT_PUBLIC_NGROK_URL environment variable in your .env file.Step 4: Set up PlanetScale MySQL database
Next, you’ll need to set up a PlanetScale-compatible MySQL database. This will be used to store user data and link metadata. There are two options:Option 1: Local MySQL database with PlanetScale simulator (recommended)
You can use a local MySQL database with a PlanetScale simulator. This is the recommended option for local development since it’s 100% free. Prerequisites:Spin up the docker-compose stack
apps/web directory and run the following command to start the Docker Compose stack:Set up database environment variables
.env file:@planetscale/database SDK.Generate Prisma client and create database tables
apps/web directory and run the following command to generate the Prisma client:Option 2: PlanetScale hosted database
Create PlanetScale database

Set up PlanetScale environment variables
DATABASE_URL into your .env file.
Generate Prisma client and create database tables
apps/web directory and run the following command to generate the Prisma client:Step 5: Set up Mailhog
To view emails sent from your application during local development, you’ll need to set up Mailhog.docker compose up as part of the database setup, you
can skip this step. Mailhog is included in the Docker Compose configuration
and should already be running.Pull Mailhog Docker image
Start Mailhog container
Step 6: Set NextAuth secret
Generate a secret by visiting https://generate-secret.vercel.app/32. Set the value ofNEXTAUTH_SECRET in .env to this value.
Step 7: Seed the database (optional)
You can seed the database with sample data for testing and development purposes. This creates a workspace with test users, domains, folders, partners, and other resources.Run the seed script
apps/web directory and run the following command:Truncate and seed (optional)
--truncate, the script will ask for confirmation before deleting any data.Step 8: Start the development server
Finally, you can start the development server. This will build the packages + start the app servers.apps/web) will be available at localhost:8888. Additionally, you may access Prisma Studio to manage your MySQL database at localhost:5555.
Logging into the application
After seeding the database and starting the development server, you can log in to the application using one of the test users created during the seed process.Find a test user email
owner@dub-internal-test.com.Get the login link
Complete login
Testing your shortlinks locally
Use the following url structure to ensure event tracking is working, and to populate analytics data, replacing<shortlink-key> with the shortlink key you’ve created.
Troubleshooting
500 error on /api/workspaces/[idOrSlug] route
If you’re receiving a 500 error when accessing workspace-related pages, it may be due to missing Stripe API keys. Check your application logs for Stripe-related errors.
For local development only, you can add mock Stripe keys to your apps/web/.env file:
Running E2E tests locally
To run end-to-end tests locally, you’ll need to configure additional environment variables and generate an API token.Add E2E environment variables
apps/web/.env file:Generate an API token
- Start your development server and log in to the application
- Navigate to http://localhost:8888/acme/settings/tokens
- Generate a new API token with full access permissions
- Replace all instances of
your_token_herein your.envfile with the generated token
About the CI variable
CI=true variable is used because some tests are designed to run in CI environments. Setting this to true allows you to run these tests locally for development and debugging purposes.