Skip to main content
Building Caddy from source gives you control over the build process and is essential for module development. This guide covers building for both development and production.

Requirements

From README.md:106:
Older Go versions are not supported. Always use the version specified in the requirements.

Quick Development Build

For rapid development and testing, from README.md:114:
1

Clone the repository

git clone "https://github.com/caddyserver/caddy.git"
cd caddy/cmd/caddy/
2

Build the binary

go build
This creates a caddy binary in the current directory.
These steps will not embed proper version information. For production builds with version info, see the next section.

Running Tests

From README.md:136:

All Tests

go test ./...

Specific Module

go test ./modules/caddyhttp/tracing/
Run tests frequently during development to catch issues early.

Permission to Bind Low Ports

Caddy may need to bind to ports 80 and 443 for HTTPS. From README.md:120:

Linux

sudo setcap cap_net_bind_service=+ep ./caddy

Using go run

If you prefer go run, use the included helper script:
go run -exec ./setcap.sh main.go

Passwordless setcap (Optional)

To avoid entering your password repeatedly, from README.md:128:
Only do this if you understand the security implications!
sudo visudo
Add this line (replace username with your actual username):
username ALL=(ALL:ALL) NOPASSWD: /usr/sbin/setcap

Production Build with Version Info

For builds with proper version information and/or plugins, use xcaddy. From README.md:145:
xcaddy build
See the xcaddy documentation for detailed information.

Manual Production Build Process

The xcaddy tool automates these steps from README.md:151:
1

Create a new folder

mkdir caddy
cd caddy
2

Copy main.go

Copy Caddy’s main.go into the folder.From cmd/caddy/main.go:15:
package main

import (
    _ "time/tzdata"
    caddycmd "github.com/caddyserver/caddy/v2/cmd"
    _ "github.com/caddyserver/caddy/v2/modules/standard"
)

func main() {
    caddycmd.Main()
}
Add imports for any custom plugins you want.
3

Initialize Go module

go mod init caddy
4

Pin Caddy version (optional)

go get github.com/caddyserver/caddy/v2@version
Replace version with:
  • A git tag (e.g., v2.8.0)
  • A commit hash
  • A branch name (e.g., master)
5

Add plugins (optional)

Add plugin imports to main.go:
import (
    _ "time/tzdata"
    caddycmd "github.com/caddyserver/caddy/v2/cmd"
    _ "github.com/caddyserver/caddy/v2/modules/standard"
    
    // Your plugins
    _ "github.com/example/caddy-plugin"
)
6

Build

go build -tags=nobadger,nomysql,nopgx

Build Tags

From README.md:159, the standard build uses these tags:
-tags=nobadger,nomysql,nopgx
These tags:
  • nobadger: Exclude BadgerDB support
  • nomysql: Exclude MySQL support
  • nopgx: Exclude PostgreSQL support
These databases are used by certain optional modules. Excluding them reduces binary size.

Custom Version Information

From caddy.go:940, you can set a custom version at build time:
go build -ldflags '-X github.com/caddyserver/caddy/v2.CustomVersion=v2.8.0-custom'
The version is reported in:
  • caddy version command
  • Admin API responses
  • Server headers (if configured)

Build Output Location

By default, go build creates the binary in the current directory:
# Build in current directory
go build

# Specify output location
go build -o /usr/local/bin/caddy

Cross-Compilation

Build for different platforms:
# Linux AMD64
GOOS=linux GOARCH=amd64 go build

# Windows AMD64  
GOOS=windows GOARCH=amd64 go build

# macOS ARM64 (Apple Silicon)
GOOS=darwin GOARCH=arm64 go build

# Linux ARM64
GOOS=linux GOARCH=arm64 go build

Optimizing Binary Size

Reduce binary size with build flags:
go build -ldflags=\"-s -w\" -trimpath
  • -s: Strip symbol table
  • -w: Strip DWARF debugging info
  • -trimpath: Remove file system paths from binary
Combine with UPX compression for even smaller binaries:
upx --best --lzma caddy

Static Linking

From README.md:93, Caddy runs with no external dependencies (not even libc):
CGO_ENABLED=0 go build
This creates a fully static binary that can run on any Linux system.

Module Imports

From modules/standard/imports.go:1, standard modules are imported:
package standard

import (
    // standard Caddy modules
    _ "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
    _ "github.com/caddyserver/caddy/v2/modules/caddyevents"
    _ "github.com/caddyserver/caddy/v2/modules/caddyevents/eventsconfig"
    _ "github.com/caddyserver/caddy/v2/modules/caddyfs"
    _ "github.com/caddyserver/caddy/v2/modules/caddyhttp/standard"
    _ "github.com/caddyserver/caddy/v2/modules/caddypki"
    _ "github.com/caddyserver/caddy/v2/modules/caddypki/acmeserver"
    _ "github.com/caddyserver/caddy/v2/modules/caddytls"
    _ "github.com/caddyserver/caddy/v2/modules/caddytls/distributedstek"
    _ "github.com/caddyserver/caddy/v2/modules/caddytls/standardstek"
    _ "github.com/caddyserver/caddy/v2/modules/filestorage"
    _ "github.com/caddyserver/caddy/v2/modules/logging"
    _ "github.com/caddyserver/caddy/v2/modules/metrics"
)
The blank imports (_) trigger init() functions that register modules.

Verification

After building, verify your binary:
# Check version
./caddy version

# List all modules
./caddy list-modules

# Validate a config
./caddy validate --config Caddyfile

# Test run
./caddy run --config Caddyfile

Troubleshooting

Version Shows “unknown”

From README.md:112, this happens with simple go build. Use xcaddy or manual steps for version info.

Module Not Found

Ensure the module is imported in main.go and run:
go mod tidy

Permission Denied on Ports

Run setcap on the binary (Linux) or use sudo.

Large Binary Size

Use optimization flags and consider excluding unused modules.

Next Steps

xcaddy Tool

Use xcaddy for easier builds with plugins

Custom Builds

Create custom builds with plugins

Module Development

Develop your own modules

Testing Guide

Test your custom builds

Build docs developers (and LLMs) love