---
title: "Development Guide"
description: "Set up your local development environment for contributing to Pangolin"
---

This guide describes how to set up your local development environment for contributing to Pangolin. We recommend using Docker Compose for the most consistent development experience across different environments.

## Prerequisites

<Check>
- Text Editor (VSCode, Neovim, etc.)
- NodeJS v20.10.0
- NPM v10.2.3 (or similar)
- Go v1.23.1
- Git
- Docker & Docker Compose
</Check>

<Info>
For managing multiple versions of Go, you may want to use [gvm](https://github.com/moovweb/gvm).
For managing multiple versions of NodeJS, you may want to use [nvm](https://github.com/nvm-sh/nvm).
</Info>

## Setup Your Repository

Below is an example if you're working on the Pangolin repository.

<Steps>
<Step title="Fork and clone">
  [Fork](https://help.github.com/articles/fork-a-repo/) the repository(ies) to your own GitHub account and [clone](https://help.github.com/articles/cloning-a-repository/) to your local device:

  ```bash
  git clone https://github.com/YOUR_USERNAME/pangolin.git
  cd pangolin/
  ```
</Step>

<Step title="Add upstream remote">
  Add the remote `upstream`:

  ```bash
  git remote add upstream https://github.com/fosrl/pangolin.git
  ```
</Step>

<Step title="Create feature branch">
  Create a new branch:

  ```bash
  git checkout -b BRANCH_NAME dev
  ```

  <Tip>
  It is recommended to give your branch a meaningful name, relevant to the feature or fix you are working on.
  
  **Good examples**:
  - `docs-docker`
  - `feature-new-system`
  - `fix-title-cards`
  
  **Bad examples**:
  - `bug`
  - `docs`
  - `feature`
  - `fix`
  - `patch`
  </Tip>
</Step>

<Step title="Open pull request">
  If you open a pull request, open it against the `dev` branch of the original repository.
</Step>
</Steps>

## Pangolin Development Setup

Choose your preferred development approach. We strongly recommend **Docker Compose** for the most consistent experience across all platforms.

### Option 1: Docker Compose (Recommended)

<CardGroup cols={2}>
<Card title="Advantages" icon="check">
  - Consistent environment
  - Easy setup and teardown
  - Isolated dependencies
  - Works on any OS
</Card>

<Card title="Requirements" icon="docker">
  - Docker installed
  - Docker Compose installed
  - 4GB+ RAM available
</Card>
</CardGroup>

<Steps>
<Step title="Install dependencies">
  Install package dependencies:

  ```bash
  npm install
  ```
</Step>

<Step title="Configure environment">
  Ensure you have a `config/` directory at the root with a `config.yml` inside. Refer to the [Pangolin Configuration docs](/self-host/advanced/config-file) or the `config.example.yml` in the repo for a sample of what to include in that file.

  <Warning>
  You may need to tweak this to run in dev, such as setting the `dashboard_url` to `http://localhost:3002`.
  </Warning>
</Step>

<Step title="Set your environment">
Choose to build from the oss/enterprise/saas codebase:
  ```bash
  npm run set:oss
  # or npm run set:enterprise
  # or npm run set:saas
  ```

Then choose your database:
  ```bash
  npm run set:sqlite
  # or npm run set:pg
  ```
</Step>

<Step title="Generate database schema">
  Generate the database schema and push it:

  ```bash
  npm run db:sqlite:generate
  npm run db:sqlite:push
  ```
</Step>

<Step title="Start development server">
  Start the development server using Docker Compose:

  ```bash
  docker compose up --build
  ```

  <Tip>
  This will build and start all services in development mode with hot reloading enabled.
  </Tip>
</Step>
</Steps>

<Note>
When running Pangolin for the first time there will be no exit nodes. This means that there have been no Gerbil "exit nodes" registered in the database. When Gerbil first starts up and requests its config from Pangolin for the first time it gets registered as an exit node.

The easiest way to resolve this is to run Gerbil and have it register in your dev environment. Download the Gerbil binary and run it with localhost:

```bash
./gerbil \
--reachableAt=http://gerbil:3003 \
--generateAndSaveKeyTo=/var/config/key \
--remoteConfig=http://pangolin:3001/api/v1/
```
</Note>

### Option 2: Local Development

<Warning>
Local development requires more setup and may have environment-specific issues. Docker Compose is recommended for consistency.
</Warning>

<Steps>
<Step title="Install dependencies">
  Install package dependencies:

  ```bash
  npm install
  ```
</Step>

<Step title="Configure environment">
  Ensure you have a `config/` directory at the root with a `config.yml` inside. Refer to the [Pangolin Configuration docs](/self-host/advanced/config-file) or the `config.example.yml` in the repo for a sample of what to include in that file.

  <Warning>
  You may need to tweak this to run in dev, such as setting the `dashboard_url` to `http://localhost:3002`.
  </Warning>
</Step>

<Step title="Generate database schema">
  Generate the database schema and push it:

  ```bash
  npm run db:sqlite:generate
  npm run db:sqlite:push
  ```
</Step>

<Step title="Start development server">
  Start the development server:

  ```bash
  npm run dev
  ```
</Step>
</Steps>

## Windows Development Considerations

<Warning>
Windows users with Docker Desktop + WSL2: File change detection may not work properly when project files are stored on the Windows filesystem.
</Warning>

<Tabs>
<Tab title="WSL2 Filesystem (Recommended)">
  **Best performance and compatibility**


  - **Where to store your project files:**
    - For best performance, always store your project inside the Linux filesystem of your Docker or Default WSL2 instance, e.g. `/home/<user>/pangolin`.
    - If other WSL instances are used, ensure the Docker Desktop WSL integration is enabled for that distribution.
    - For further information, see Link Section below.

  - **Accessing WSL2 files from Windows:**
    - You can access your WSL2 home directory from Windows using the UNC path: `\\wsl$\<DistroName>\home\<user>\pangolin` (replace `<DistroName>` with your actual WSL distribution, e.g. `Ubuntu-22.04`).
    - This path works in Windows Explorer, VS Code, and other Windows applications. You can drag & drop files, create shortcuts, or map a network drive for convenience.
    - **Note:** This UNC path is for Windows tools only. Do not use it for Docker container mounts.

  - **How to mount WSL2 files in Docker containers:**
    - Always use the absolute Linux path from inside WSL2 for Docker volumes. This is the only method fully supported and recommended by Docker.
    - **Correct Docker Compose example:**
      ```yaml
      services:
        app:
          volumes:
            - /home/<user>/pangolin:/app
      ```
    - **Correct docker run example:**
      ```bash
      docker run -v /home/<user>/pangolin:/app my-image
      ```
    - **Never use `\\wsl$` or Windows paths** (e.g. `/mnt/c/...`) for Docker volumes when running with the WSL2 backend. This is not supported and can lead to poor performance or errors.
    - File watchers and hot reload works natively when your project is inside the WSL2 filesystem and mounted using the Linux path.

  <Note> You may want to use the [VS Code Remote - WSL extension](https://code.visualstudio.com/docs/remote/wsl) or [VS Code Remote - SSH Extension](https://code.visualstudio.com/docs/remote/ssh) to open your project folder directly in VSCode from the WSL/Remote Filesystem for seamless Development. </Note>


  **Reference Links**
  - [WSL Docker Best Practices](https://docs.docker.com/desktop/features/wsl/best-practices/)
  - [Use WSL for Development](https://docs.docker.com/desktop/features/wsl/use-wsl/)
  - [WSL2 Setup](https://docs.docker.com/desktop/features/wsl/)
</Tab>

<Tab title="Windows Filesystem + Polling (Workaround)">
  If you need to keep your files on the native Windows filesystem (`C:\Users\...`), enable **Polling Mode** for file watchers. 

  Enable polling mode by adding the following environment variables to your `docker-compose.yml` or `.env` file:

  For `.env`:
  ```env
    WATCHPACK_POLLING=true
    CHOKIDAR_USEPOLLING=true
  ```
  
  For `docker-compose.yml`:
  ```yaml
  environment:
    - WATCHPACK_POLLING=true
    - CHOKIDAR_USEPOLLING=true
  ```

  <Note>This increases CPU usage but ensures file watchers work properly. Polling mode is not required when working directly on the WSL filesystem.</Note>
</Tab>
</Tabs>

## Component Development

### Gerbil

<Card title="Requirements">
  - Go v1.23.1
</Card>

```bash
make local
```

### Newt

<Card title="Requirements">
  - Go v1.23.1
</Card>

```bash
make local
```

### Olm

<Card title="Requirements">
  - Go v1.23.1
</Card>

```bash
make local
```
