Skip to content

A simple, open-source tool for programatically managing whitelabel custom domains for SaaS applications using Caddy.

License

Notifications You must be signed in to change notification settings

avashForReal/caddy-control

Repository files navigation

Caddy Control Logo

Caddy Control - Open Source Domain Routing Control Service

About this project

This project was born out of the need to build a custom domain service with automated SSL management tailored for white-label SaaS platforms. There are managed services like Approximated, SaaS Custom Domains, etc., but limited open-source, self-hosted alternatives that provide a reliable experience. Leveraging Caddy, this project provides a way to programmatically integrate 'bring your own domain' features into SaaS products, as well as manage proxies for regular routing needs.

Features

  • Rest API Access
  • Proxy management dashboard
  • API Keys management dashboard
  • Domain redirection option
  • Multi user support

Screenshots

scr1

scr2

Tech Stack

Local Development

To run caddy control locally, you will need to setup the following:

  • Caddy - Recommended to run using the included development docker compose file.
  • Docker - Recommended but not mandatory.

Recommended: Run the caddy server docker image locally using the following command:

pnpm dev:caddy

It uses the docker-compose.dev.yml compose file to start a local instance of caddy server.

Once the required setup is done, run the following commands to start caddy control locally.

  1. Clone the repository
git clone https://github.com/avashForReal/caddy-control.git
  1. Create a .env file with the following content.
APP_HOST=<YOUR-APP_DOMAIN>
CADDY_SERVER_IP=localhost
CADDY_ADMIN_URL=http://localhost:2019
JWT_SECRET="awesomesecret"

Description:
APP_HOST: Not relevant for local development.
CADDY_SERVER_IP: IP of the caddy server. If you are using a remote server then put the public IP of the server.
CADDY_ADMIN_URL: URL of the caddy admin API. If you are using a remote server then put the public IP of the server.
JWT_SECRET: JWT token secret.

  1. Install Dependencies
pnpm install
  1. Start development server
pnpm dev

The first user will me seeded with admin username and admin password and will be prompted to change password after first login.

Self Hosting Using Docker

End-to-end guide on how to self-host caddy control.

Prerequisites

To self host you will need:

  • A server with public a IP. Make sure to expose the ports 80 and 443 for incoming traffic.
  • Docker installed in your server.

Run the following script to get up and running using docker.

bash -c "$(curl -sSL https://raw.githubusercontent.com/avashForReal/caddy-control/refs/heads/main/deploy.sh)"

Setup

Server IP: Public IP address of the server where the deploy command is being executed.
App Host domain: Domain which will be used to access caddy control. This domain must have an A record pointing to the server IP.
JWT Secret: Use tool like JWTSecret to generate one or enter your own secret string.

Make sure to create an A record for the provided App Host Domain pointing to Server IP. Caddy control will be available at the provided App Host Domain.

All created hosts will have to create an A record pointing to Server IP to access the configured upstream.

After the setup is complete, first user with following credentials will be created:

username: admin
password: admin

This user will be prompted for password change in their first login.

API Documentation

Authentication

  • All API requests must include an x-api-key header.

Endpoints

Add Domain

Endpoint: /api/domains
Method: POST
Headers: { "x-api-key": "your_api_key" }
Request Body:

{
  "incomingAddress": "yourdomain.com",
  "destinationAddress": "backend.com",
  "port": 8080,
  "enableHttps": true
}

Description:

  • incomingAddress: The domain name that users will access. This must be a valid domain (e.g., abc.mywhitelabledomain.com). This domain also should have an A record pointing to the IP address of server where caddy control is running.
  • destinationAddress: The backend server where requests should be routed. This must also be a valid domain (e.g., customer.saasprovider.com).
  • port: The port number of the backend server that will handle incoming traffic (e.g., 8080). If the desination has SSL enabled then this should probably be set to 443.
  • enableHttps (optional): Determines whether HTTPS should be enabled for the domain. Defaults to true if not provided.

Response:

{ "message": "Domain added successfully!" }

Get Caddy Configuration

Endpoint: /api/caddy/config
Method: GET
Headers: { "x-api-key": "your_api_key" }
Response:

{
  "config": {
    /* Caddy Configuration */
  }
}

Get Registered Domains

Endpoint: /api/domains
Method: GET
Headers: { "x-api-key": "your_api_key" }
Response:

{
  "data": [
    {
      "id": "cm8o9l9d50001sy2uorjt2wua",
      "incomingAddress": "demo.incomingaddress.com",
      "destinationAddress": "xyz.destination.com",
      "port": 443,
      "isLocked": false,
      "enableHttps": true,
      "createdAt": "2025-03-25T08:59:25.481Z",
      "checkResults": {
        "dnsCheck": {
          "result": false,
          "description": "Domain does not resolve to proxy IP."
        },
        "proxyReachability": {
          "result": false,
          "description": "Requests do not reach the proxy."
        }
      }
    }
  ],
  "total": 1
}

Delete Domain

Endpoint: /api/domains
Method: DELETE
Headers: { "x-api-key": "your_api_key" }
Request Body:

{ "incomingAddress": "yourdomain.com" }

Description:

  • incomingAddress: The domain address that you want to delete.

Response:

{ "message": "Domain deleted successfully!" }

Error Handling

Responses follow this format:

{
  "error": "Error message",
  "details": [
    /* Validation errors */
  ]
}

License

MIT

About

A simple, open-source tool for programatically managing whitelabel custom domains for SaaS applications using Caddy.

Resources

License

Stars

Watchers

Forks

Packages

No packages published