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:
Clone the repository
git clone "https://github.com/caddyserver/caddy.git"
cd caddy/cmd/caddy/
Build the binary
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
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!
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:
See the xcaddy documentation for detailed information.
Manual Production Build Process
The xcaddy tool automates these steps from README.md:151:
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.
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)
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 "
)
Build
go build -tags=nobadger,nomysql,nopgx
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.
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:
Static Linking
From README.md:93, Caddy runs with no external dependencies (not even libc):
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:
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