---
title: "Configuration File"
description: "Configure Pangolin using the config.yml file with detailed settings for all components"
---

The `config.yml` file controls all aspects of your Pangolin deployment, including server settings, domain configuration, email setup, and security options. This file is mounted at `config/config.yml` in your Docker container.

## Setting up your `config.yml`

To get started, create a basic configuration file with the essential settings:

Minimal Pangolin configuration:

```yaml title="config.yml"
app:
  dashboard_url: "http://pangolin.example.com"

domains:
  domain1:
    base_domain: "pangolin.example.com"
    cert_resolver: "letsencrypt"

server:
  secret: "your-strong-secret"

gerbil:
  base_endpoint: "pangolin.example.com"

flags:
  require_email_verification: false
  disable_signup_without_invite: true
  disable_user_create_org: true
```


<Warning>
Generate a strong secret for `server.secret`. Use at least 32 characters with a mix of letters, numbers, and special characters.
</Warning>

## Reference

This section contains the complete reference for all configuration options in `config.yml`.

### Application Settings

<ResponseField name="app" type="object" required>
  Core application configuration including dashboard URL, logging, and general settings.

  <Expandable title="App">
    <ResponseField name="dashboard_url" type="string" required>
      The URL where your Pangolin dashboard is hosted.

      **Examples**: `https://example.com`, `https://pangolin.example.com`

      This URL is used for generating links, redirects, and authentication flows. You can run Pangolin on a subdomain or root domain.
    </ResponseField>

    <ResponseField name="log_level" type="string">
      The logging level for the application.

      **Options**: `debug`, `info`, `warn`, `error`

      **Default**: `info`
    </ResponseField>

    <ResponseField name="save_logs" type="boolean">
      Whether to save logs to files in the `config/logs/` directory.

      **Default**: `false`

      <Note>
        When enabled, logs rotate automatically:
        - Max file size: 20MB
        - Max files: 7 days
      </Note>
    </ResponseField>

    <ResponseField name="log_failed_attempts" type="boolean">
      Whether to log failed authentication attempts for security monitoring.

      **Default**: `false`
    </ResponseField>
  </Expandable>
</ResponseField>

### Server Configuration

<ResponseField name="server" type="object" required>
  Server ports, networking, and authentication settings.

  <Expandable title="Server">
    <ResponseField name="external_port" type="integer">
      The port for the front-end API that handles external requests.

      **Example**: `3000`
    </ResponseField>

    <ResponseField name="internal_port" type="integer">
      The port for the internal private-facing API.

      **Example**: `3001`
    </ResponseField>

    <ResponseField name="next_port" type="integer">
      The port for the frontend server (Next.js).

      **Example**: `3002`
    </ResponseField>

    <ResponseField name="integration_port" type="integer">
      The port for the integration API (optional).

      **Example**: `3003`
    </ResponseField>

    <ResponseField name="internal_hostname" type="string">
      The hostname of the Pangolin container for internal communication.

      **Example**: `pangolin`

      <Tip>
        If using Docker Compose, this should match your container name.
      </Tip>
    </ResponseField>

    <ResponseField name="session_cookie_name" type="string">
      The name of the session cookie for storing authentication tokens.

      **Example**: `p_session_token`

      **Default**: `p_session_token`
    </ResponseField>

    <ResponseField name="resource_access_token_param" type="string">
      Query parameter name for passing access tokens in requests.

      **Example**: `p_token`

      **Default**: `p_token`
    </ResponseField>

    <ResponseField name="resource_access_token_headers" type="object">
      HTTP headers for passing access tokens in requests.

      <Expandable title="Headers">
        <ResponseField name="id" type="string">
          Header name for access token ID.

          **Example**: `P-Access-Token-Id`
        </ResponseField>

        <ResponseField name="token" type="string">
          Header name for access token.

          **Example**: `P-Access-Token`
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="resource_session_request_param" type="string">
      Query parameter for session request tokens.

      **Example**: `p_session_request`

      **Default**: `p_session_request`
    </ResponseField>

    <ResponseField name="cors" type="object">
      Cross-Origin Resource Sharing (CORS) configuration.

      <Expandable title="CORS">
        <ResponseField name="origins" type="array of strings">
          Allowed origins for cross-origin requests.

          **Example**: `["https://pangolin.example.com"]`
        </ResponseField>

        <ResponseField name="methods" type="array of strings">
          Allowed HTTP methods for CORS requests.

          **Example**: `["GET", "POST", "PUT", "DELETE", "PATCH"]`
        </ResponseField>

        <ResponseField name="allowed_headers" type="array of strings">
          Allowed HTTP headers in CORS requests.

          **Example**: `["X-CSRF-Token", "Content-Type"]`
        </ResponseField>

        <ResponseField name="credentials" type="boolean">
          Whether to allow credentials in CORS requests.

          **Default**: `true`
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="trust_proxy" type="integer">
      Number of proxy headers to trust for client IP detection.

      **Example**: `1`

      **Default**: `1`

      <Tip>
        Use `1` if running behind a single reverse proxy like Traefik.
      </Tip>
    </ResponseField>

    <ResponseField name="dashboard_session_length_hours" type="integer">
      Dashboard session duration in hours.

      **Example**: `720` (30 days)

      **Default**: `720`
    </ResponseField>

    <ResponseField name="resource_session_length_hours" type="integer">
      Resource session duration in hours.

      **Example**: `720` (30 days)

      **Default**: `720`
    </ResponseField>

    <ResponseField name="secret" type="string" required>
      Secret key for encrypting sensitive data.

      **Environment Variable**: `SERVER_SECRET`

      **Minimum Length**: 8 characters

      **Example**: `"d28@a2b.2HFTe2bMtZHGneNYgQFKT2X4vm4HuXUXBcq6aVyNZjdGt6Dx-_A@9b3y"`

      <Warning>
        Generate a strong, random secret. This is used for encrypting sensitive data and should be kept secure.
      </Warning>
    </ResponseField>
  </Expandable>
</ResponseField>

### Domain Configuration

<ResponseField name="domains" type="object" required>
  Domain settings for SSL certificates and routing.

  At least one domain must be configured.

  <Expandable title="Domains">
    <ResponseField name="<domain_key>" type="object">
      Domain configuration with a unique key of your choice.

      <Expandable title="Domain Settings">
        <ResponseField name="base_domain" type="string" required>
          The base domain for this configuration.

          **Example**: `example.com`
        </ResponseField>

        <ResponseField name="cert_resolver" type="string" required>
          The Traefik certificate resolver name.

          **Example**: `letsencrypt`

          <Note>
            This must match the certificate resolver name in your Traefik configuration.
          </Note>
        </ResponseField>

        <ResponseField name="prefer_wildcard_cert" type="boolean">
          Whether to prefer wildcard certificates for this domain.

          **Example**: `true`

          <Tip>
            Useful for domains with many subdomains to reduce certificate management overhead.
          </Tip>
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

### Traefik Integration

<ResponseField name="traefik" type="object">
  Traefik reverse proxy configuration settings.

  <Expandable title="Traefik">
    <ResponseField name="http_entrypoint" type="string">
      The Traefik entrypoint name for HTTP traffic.

      **Example**: `web`

      <Note>
        Must match the entrypoint name in your Traefik configuration.
      </Note>
    </ResponseField>

    <ResponseField name="https_entrypoint" type="string">
      The Traefik entrypoint name for HTTPS traffic.

      **Example**: `websecure`

      <Note>
        Must match the entrypoint name in your Traefik configuration.
      </Note>
    </ResponseField>

    <ResponseField name="cert_resolver" type="string">
      The default certificate resolver for domains created through the UI.

      **Example**: `letsencrypt`

      <Note>
        This only applies to domains created through the Pangolin dashboard.
      </Note>
    </ResponseField>

    <ResponseField name="prefer_wildcard_cert" type="boolean">
      Whether to prefer wildcard certificates for UI-created domains.

      **Example**: `true`

      <Note>
        This only applies to domains created through the Pangolin dashboard.
      </Note>
    </ResponseField>

    <ResponseField name="additional_middlewares" type="array of strings">
      Additional Traefik middlewares to apply to resource routers.

      **Example**: `["middleware1", "middleware2"]`

      <Note>
        These middlewares must be defined in your Traefik dynamic configuration.
      </Note>
    </ResponseField>
  </Expandable>
</ResponseField>

### Gerbil Tunnel Controller

<ResponseField name="gerbil" type="object" required>
  Gerbil tunnel controller settings for WireGuard tunneling.

  <Expandable title="Gerbil">
    <ResponseField name="base_endpoint" type="string" required>
      Domain name included in WireGuard configuration for tunnel connections.

      **Example**: `pangolin.example.com`
    </ResponseField>

    <ResponseField name="start_port" type="integer">
      Starting port for WireGuard tunnels.

      **Example**: `51820`
    </ResponseField>

    <ResponseField name="use_subdomain" type="boolean">
      Whether to assign unique subdomains to Gerbil exit nodes.

      **Default**: `false`

      <Warning>
        Keep this set to `false` for most deployments.
      </Warning>
    </ResponseField>

    <ResponseField name="subnet_group" type="string">
      IP address CIDR range for Gerbil exit node subnets.

      **Example**: `10.0.0.0/8`
    </ResponseField>

    <ResponseField name="block_size" type="integer">
      Block size for Gerbil exit node CIDR ranges.

      **Example**: `24`
    </ResponseField>

    <ResponseField name="site_block_size" type="integer">
      Block size for site CIDR ranges connected to Gerbil.

      **Example**: `26`
    </ResponseField>
  </Expandable>
</ResponseField>

### Rate Limiting

<ResponseField name="rate_limits" type="object">
  Rate limiting configuration for API requests.

  <Expandable title="Rate Limits">
    <ResponseField name="global" type="object">
      Global rate limit settings for all external API requests.

      <Expandable title="Global">
        <ResponseField name="window_minutes" type="integer">
          Time window for rate limiting in minutes.

          **Example**: `1`
        </ResponseField>

        <ResponseField name="max_requests" type="integer">
          Maximum number of requests allowed in the time window.

          **Example**: `100`
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

### Email Configuration

<ResponseField name="email" type="object">
  SMTP settings for sending transactional emails.

  <Expandable title="Email">
    <ResponseField name="smtp_host" type="string">
      SMTP server hostname.

      **Example**: `smtp.gmail.com`
    </ResponseField>

    <ResponseField name="smtp_port" type="integer">
      SMTP server port.

      **Example**: `587` (TLS) or `465` (SSL)
    </ResponseField>

    <ResponseField name="smtp_user" type="string">
      SMTP username.

      **Example**: `no-reply@example.com`
    </ResponseField>

    <ResponseField name="smtp_pass" type="string">
      SMTP password.

      **Environment Variable**: `EMAIL_SMTP_PASS`
    </ResponseField>

    <ResponseField name="smtp_secure" type="boolean">
      Whether to use secure connection (SSL/TLS).

      **Default**: `false`

      <Tip>
        Enable this when using port 465 (SSL).
      </Tip>
    </ResponseField>

    <ResponseField name="no_reply" type="string">
      From address for sent emails.

      **Example**: `no-reply@example.com`

      <Note>
        Usually the same as `smtp_user`.
      </Note>
    </ResponseField>

    <ResponseField name="smtp_tls_reject_unauthorized" type="boolean">
      Whether to fail on invalid server certificates.

      **Default**: `true`
    </ResponseField>
  </Expandable>
</ResponseField>

### Feature Flags

<ResponseField name="flags" type="object">
  Feature flags to control application behavior.

  <Expandable title="Flags">
    <ResponseField name="require_email_verification" type="boolean">
      Whether to require email verification for new users.

      **Default**: `false`

      <Warning>
        Only enable this if you have email configuration set up.
      </Warning>
    </ResponseField>

    <ResponseField name="disable_signup_without_invite" type="boolean">
      Whether to disable public user registration.

      **Default**: `false`

      <Note>
        Users can still sign up with valid invites when enabled.
      </Note>
    </ResponseField>

    <ResponseField name="disable_user_create_org" type="boolean">
      Whether to prevent users from creating organizations.

      **Default**: `false`

      <Note>
        Server admins can always create organizations.
      </Note>
    </ResponseField>

    <ResponseField name="allow_raw_resources" type="boolean">
      Whether to allow raw TCP/UDP resource creation.

      **Default**: `true`

      <Note>
        If set to `false`, users will only be able to create http/https resources.
      </Note>
    </ResponseField>

    <ResponseField name="enable_integration_api" type="boolean">
      Whether to enable the integration API.

      **Default**: `false`
    </ResponseField>
  </Expandable>
</ResponseField>

### Database Configuration

<ResponseField name="postgres" type="object">
  PostgreSQL database configuration (optional).

  <Expandable title="PostgreSQL">
    <ResponseField name="connection_string" type="string" required>
      PostgreSQL connection string.

      **Example**: `postgresql://user:password@host:port/database`

      <Note>
        See [PostgreSQL documentation](../database/postgres) for setup instructions.
      </Note>
    </ResponseField>
  </Expandable>
</ResponseField>

## Environment Variables

Some configuration values can be set using environment variables for enhanced security:

<CardGroup cols={2}>
<Card title="Server Secret" icon="key">
  **Variable**: `SERVER_SECRET`
  
  **Config**: `server.secret`
  
  Use this to avoid hardcoding secrets in your config file.
</Card>

<Card title="Email Password" icon="mail">
  **Variable**: `EMAIL_SMTP_PASS`
  
  **Config**: `email.smtp_pass`
  
  Keep SMTP passwords secure using environment variables.
</Card>
</CardGroup>
