Menu
dokploy-to-pangolin docs

Dokploy Hook Pangolin

Docker Hub

A webhook service that automatically registers new Dokploy applications with Pangolin VPN, creating resources and targets for seamless integration.

Overview

This service acts as a bridge between Dokploy (deployment platform) and Pangolin VPN (VPN/tunneling service). When Dokploy deploys a new application, it sends a webhook to this service, which automatically:

  1. Receives build notifications from Dokploy
  2. Checks if the domain already exists in Pangolin
  3. Creates a new Pangolin resource if needed
  4. Configures a resource target pointing to your main site
  5. Enables automatic routing through Pangolin VPN

Architecture

┌──────────┐         ┌─────────────────┐         ┌──────────┐
│ Dokploy  │────────▶│  This Service   │────────▶│ Pangolin │
│          │ Webhook │                 │   API   │   VPN    │
└──────────┘         └─────────────────┘         └──────────┘

                            │ Validates

                     ┌──────────────┐
                     │   Config     │
                     │ Environment  │
                     └──────────────┘

Features

  • Automatic resource creation in Pangolin when new domains are deployed
  • Intelligent domain matching to avoid duplicates
  • Duplicate detection and cleanup for resources that already got cloned
  • Webhook authentication with secret token
  • Comprehensive error handling and logging
  • Graceful shutdown handling (SIGINT/SIGTERM)
  • Full TypeScript support with custom types
  • 90%+ test coverage
  • Docker & Docker Compose support
  • Built with Bun & Hono for performance

Prerequisites

  • A Pangolin account or self-hosted instance with API access
  • A Dokploy instance
  • Docker

Deployment

IMPORTANT: This service must be accessible by Dokploy to receive webhooks. You can deploy it through Dokploy & Pangolin with no issues, but if you want to run it separately, ensure it's reachable at the configured URL.

With Docker Run

docker run -p 3000:3000 \
  -e WEBHOOK_SECRET=your-secret \
  -e PANGOLIN_API_KEY=your-key \
  -e PANGOLIN_ORG_ID=your-org \
  -e PANGOLIN_MAIN_SITE_NAME=your-site \
  -e PANGOLIN_API_BASE_URL=https://api.pangolin.com \
  orochibraru/dokploy-to-pangolin

With Docker Compose

  1. Create a compose.yml file:
services:
    hook:
        image: orochibraru/dokploy-to-pangolin:latest
        ports:
            - "3000:3000"
        environment:
            - PORT=3000
            - WEBHOOK_SECRET=${WEBHOOK_SECRET}
            - PANGOLIN_API_KEY=${PANGOLIN_API_KEY}
            - PANGOLIN_ORG_ID=${PANGOLIN_ORG_ID}
            - PANGOLIN_MAIN_SITE_NAME=${PANGOLIN_MAIN_SITE_NAME}
            - PANGOLIN_API_BASE_URL=${PANGOLIN_API_BASE_URL}

Configuration

Configure the following environment variables in your .env file:

VariableDescriptionExample
PANGOLIN_API_KEYYour Pangolin API keypk_live_abc123...
PANGOLIN_ORG_IDYour Pangolin organization IDorg_xyz789
PANGOLIN_MAIN_SITE_NAMEName of your main Pangolin siteproduction-site
PANGOLIN_API_BASE_URLPangolin API base URLhttps://api.pangolin.com
WEBHOOK_SECRETSecret token for webhook authenticationyour-secure-secret-here
PORTServer port (optional, default: 3000)3000

Getting Pangolin Credentials

  1. If self-hosting, ensure the API integration is setup correctly
  2. Log in to your Pangolin dashboard
  3. Navigate to API settings
  4. Generate a new API key
  5. Note your organization ID from the URL or settings

Configuring Dokploy

  1. In your Dokploy project settings, navigate to Webhooks
  2. Add a new webhook:
    • URL: http://your-service-url:3000/webhook
    • Headers: Set x-webhook-secret to your configured WEBHOOK_SECRET environment variable
    • Actions: Select "App Deploy" events
  3. Save and deploy your application
  4. Check the logs to verify webhook processing

API Endpoints

GET /

Health check endpoint

Response:

OK

POST /webhook

Webhook endpoint for Dokploy events

Headers:

  • x-webhook-secret: Must match WEBHOOK_SECRET environment variable

Request Body:

{
    "title": "Build Success",
    "message": "Application deployed successfully",
    "timestamp": "2026-03-03T12:00:00Z",
    "type": "build",
    "status": "success",
    "projectName": "my-app",
    "domains": "my-app.example.com"
}

Response:

  • 200 OK: Webhook processed successfully
  • 401 Unauthorized: Invalid webhook secret
  • 500 Internal Server Error: Processing error

GET /duplicates

Reports resources that share a host name, without changing anything.

Headers:

  • x-webhook-secret: Must match WEBHOOK_SECRET environment variable

Response:

{
    "scanned": 256,
    "applied": false,
    "deleted": 0,
    "renamed": [],
    "failed": 0,
    "groups": [
        {
            "fullDomain": "app.example.com",
            "keep": { "resourceId": 61, "name": "my-app" },
            "duplicates": [{ "resourceId": 91, "name": "my-app" }]
        }
    ]
}

DELETE /duplicates

Same report, but deletes the duplicates and tidies up the names that survive. See Duplicate cleanup.

Duplicate cleanup

Two resources sharing a host name are a routing conflict: Pangolin serves whichever one it happens to pick. The oldest resource in each group is kept, because it is the one that was configured deliberately - its SSO, auth and rule settings are the ones you set - and the later copies are deleted.

Review first, then apply:

# Report what would change
bun run reconcile

# Delete the duplicates and tidy up the surviving names
bun run reconcile --apply

# Delete the duplicates but leave names alone
bun run reconcile --apply --no-rename

Renaming collapses repeated words in a name, so sergios-sergios-sergios becomes sergios. It has only the existing name to work from, so it would also shorten a deliberate new-project-new-app - which is why the dry run comes first.

The service logs a warning at startup when duplicates exist, but never deletes anything on its own.

How It Works

Workflow

  1. Dokploy Deploy Event

    • User deploys an application in Dokploy
    • Dokploy sends a webhook to /webhook endpoint
  2. Webhook Validation

    • Service validates the webhook secret
    • Checks if the event type is build
    • Verifies build status is not error
  3. Domain Check

    • Fetches existing Pangolin resources
    • Checks if domain already exists
  4. Resource Creation (if needed)

    • Extracts subdomain from full domain
    • Creates a new Pangolin resource
    • Configures HTTP routing and sticky sessions
  5. Target Setup

    • Creates a resource target pointing to main site
    • Configures HTTPS on port 443
    • Enables the target

Example Flow

1. Deploy "api-service" with domain "api.example.com"

2. Webhook received with domains: "api.example.com"

3. Check Pangolin resources

4. No match found → Create new resource
   - Name: "api-service"
   - Subdomain: "api"
   - Domain: "example.com"

5. Create resource target
   - Site: main-site
   - Port: 443
   - Method: HTTPS

6. ✅ "api.example.com" now routes through Pangolin

Troubleshooting

Common Issues

"Unauthorized webhook attempt detected"

  • Ensure WEBHOOK_SECRET matches in both Dokploy and this service
  • Check the x-webhook-secret header is being sent

"Cannot create resource without main domain"

  • Ensure the domain exists in your Pangolin instance/account

"No subdomain extracted from event domains"

  • Domain format should be: subdomain.maindomain.com

Duplicate resources keep appearing for the same domain

  • Fixed: the resource listing now pages through the whole collection. The API defaults to 20 items per page, so the service used to miss anything past the first page and re-create resources it already had
  • Events are also handled one at a time, so two overlapping deploys can no longer both decide a resource is missing
  • Run bun run reconcile to clean up copies made before the fix

Type errors about "never" types

  • This is expected - the Pangolin OpenAPI spec is poorly defined
  • We use custom types in src/lib/types.ts with @ts-expect-error comments

Security Considerations

  • Store sensitive credentials in environment variables, never commit them
  • Use a strong, random WEBHOOK_SECRET (openssl rand -hex 32 for example)
  • Consider using HTTPS in production (reverse proxy recommended)
  • Rotate API keys periodically
  • Limit network access to webhook endpoint if possible

Development

Getting started

  1. Clone the repository:
git clone https://github.com/orochibraru/dokploy-to-pangolin.git
cd dokploy-to-pangolin
  1. Install dependencies:
bun install
  1. Copy the example environment file:
cp .example.env .env
  1. Configure your environment variables (see Configuration)

  2. Start the development server:

bun run dev

Available Scripts

# Start development server with auto-reload
bun run dev

# Build production bundle
bun run build

# Start production server
bun run start

# Run tests
bun test

# Lint and format code
bun run lint

# Auto-fix linting issues
bun run lint:fix

# Type check
bun run check

# Generate Pangolin API types (from OpenAPI spec)
bun run gen:api

# Report duplicate resources (add --apply to clean them up)
bun run reconcile

Testing

The project has comprehensive test coverage (98.93%):

# Run all tests
bun test

# Run tests with coverage
bun test --coverage

Tech Stack

  • Runtime: Bun - Fast JavaScript runtime
  • Framework: Hono - Lightweight web framework
  • API Client: openapi-fetch - Type-safe API client
  • Linter: Biome - Fast linter and formatter
  • Testing: Bun's built-in test runner
  • Container: Docker with Alpine Linux

A note on AI generated code

Some code in this project was generated with the help of AI tools. This only concerns tests and markdown (which is already bad enough). The core logic and implementation were written by hand. Please follow this pattern when contributing to ensure code quality and consistency, to ensure we don't mutually destroy our Homelabs ❤️

Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Make your changes and add tests
  4. Run tests: bun test
  5. Run linter: bun run lint:fix
  6. Commit your changes: git commit -m 'Add amazing feature'
  7. Push to the branch: git push origin feature/amazing-feature
  8. Open a Pull Request

Support

For issues or questions:

  • Open an issue on GitHub
  • Check the troubleshooting section
  • Review Pangolin API documentation

Built with ❤️ using Bun and TypeScript

This guide lives in the project repo: edit it there, and this page follows within a day.