Custom Caddy builds allow you to include only the modules you need, add third-party plugins, and optimize for your deployment environment.
Why Custom Builds?
From README.md:175 and the architecture:
Extensibility : Add functionality without modifying Caddy’s source code
Optimization : Include only needed modules to reduce binary size
Integration : Bundle organization-specific plugins
Version control : Pin specific versions of Caddy and plugins
Performance : Compile for specific architectures
Caddy’s highly extensible modular architecture from README.md:92 makes custom builds practical and maintainable.
Build Methods Overview
xcaddy (Recommended) Fast, automated builds with plugin management
Manual Process Full control over build configuration
Using xcaddy (Recommended)
From README.md:145, xcaddy is the official build tool.
Install xcaddy
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
Basic Custom Build
xcaddy build v2.8.0 \
--with github.com/caddy-dns/cloudflare \
--with github.com/greenpau/caddy-security \
--output /usr/local/bin/caddy
See the xcaddy documentation for complete details.
Manual Custom Build
For full control, follow these steps from README.md:151 and cmd/caddy/main.go:15:
Create build directory
mkdir caddy-custom
cd caddy-custom
Create main.go
Based on cmd/caddy/main.go: package main
import (
_ " time/tzdata "
caddycmd " github.com/caddyserver/caddy/v2/cmd "
// Standard modules
_ " github.com/caddyserver/caddy/v2/modules/standard "
// Custom plugins
_ " github.com/caddy-dns/cloudflare "
_ " github.com/greenpau/caddy-security "
_ " github.com/mholt/caddy-ratelimit "
)
func main () {
caddycmd . Main ()
}
The blank import _ triggers the plugin’s init() function which registers the module.
Add plugin dependencies
go get github.com/caddy-dns/cloudflare@latest
go get github.com/greenpau/caddy-security@latest
go get github.com/mholt/caddy-ratelimit@latest
Build
From README.md:159: go build \
-tags=nobadger,nomysql,nopgx \
-ldflags= "-s -w" \
-trimpath \
-o caddy
Standard Modules
From modules/standard/imports.go:1, standard modules include:
import (
_ " 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 "
)
Do not remove modules/standard unless you know exactly which modules you need. Many features depend on these.
Minimal Build
For embedded systems or containers, create a minimal build:
package main
import (
_ " time/tzdata "
caddycmd " github.com/caddyserver/caddy/v2/cmd "
// Only essential modules
_ " github.com/caddyserver/caddy/v2/caddyconfig/caddyfile "
_ " github.com/caddyserver/caddy/v2/modules/caddyhttp "
_ " github.com/caddyserver/caddy/v2/modules/caddyhttp/fileserver "
_ " github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy "
_ " github.com/caddyserver/caddy/v2/modules/caddytls "
)
func main () {
caddycmd . Main ()
}
Build with maximum optimization:
CGO_ENABLED = 0 go build \
-tags=nobadger,nomysql,nopgx \
-ldflags= "-s -w -X github.com/caddyserver/caddy/v2.CustomVersion=v2.8.0-minimal" \
-trimpath
Popular Plugin Combinations
DNS Provider Build
For automated TLS with DNS-01 challenges:
import (
_ " github.com/caddyserver/caddy/v2/modules/standard "
// DNS providers
_ " github.com/caddy-dns/cloudflare "
_ " github.com/caddy-dns/route53 "
_ " github.com/caddy-dns/digitalocean "
)
Enhanced Security Build
import (
_ " github.com/caddyserver/caddy/v2/modules/standard "
// Security enhancements
_ " github.com/greenpau/caddy-security " // Auth portal, MFA
_ " github.com/mholt/caddy-ratelimit " // Rate limiting
_ " github.com/porech/caddy-maxmind-geolocation " // GeoIP
)
Monitoring Build
import (
_ " github.com/caddyserver/caddy/v2/modules/standard "
// Monitoring and observability
_ " github.com/caddyserver/caddy/v2/modules/caddyhttp/tracing " // OpenTelemetry
)
The tracing module is from modules/caddyhttp/tracing/module.go:15.
Config Adapter Build
import (
_ " github.com/caddyserver/caddy/v2/modules/standard "
// Config adapters
_ " github.com/caddyserver/nginx-adapter " // Nginx config
_ " github.com/caddyserver/yaml-adapter " // YAML config
)
From caddy.go:940, inject custom version info:
go build -ldflags "-X github.com/caddyserver/caddy/v2.CustomVersion=v2.8.0-company-$( date +%Y%m%d)"
The version appears in:
./caddy version
# v2.8.0-company-20260301
Linux AMD64
GOOS = linux GOARCH = amd64 go build \
-tags=nobadger,nomysql,nopgx \
-ldflags= "-s -w" \
-o caddy-linux-amd64
Windows AMD64
GOOS = windows GOARCH = amd64 go build \
-tags=nobadger,nomysql,nopgx \
-ldflags= "-s -w" \
-o caddy-windows-amd64.exe
macOS ARM64 (Apple Silicon)
GOOS = darwin GOARCH = arm64 go build \
-tags=nobadger,nomysql,nopgx \
-ldflags= "-s -w" \
-o caddy-darwin-arm64
Linux ARM64 (ARM servers)
GOOS = linux GOARCH = arm64 go build \
-tags=nobadger,nomysql,nopgx \
-ldflags= "-s -w" \
-o caddy-linux-arm64
Linux ARM (Raspberry Pi)
GOOS = linux GOARCH = arm GOARM = 7 go build \
-tags=nobadger,nomysql,nopgx \
-ldflags= "-s -w" \
-o caddy-linux-armv7
Build Optimization
Reduce Binary Size
go build \
-tags=nobadger,nomysql,nopgx \
-ldflags= "-s -w" \
-trimpath
Flags:
-tags: Exclude optional dependencies
-ldflags="-s -w": Strip debug info
-trimpath: Remove file paths
Further compress with UPX:
Static Binary
From README.md:93, build with no external dependencies :
CGO_ENABLED = 0 go build \
-tags=nobadger,nomysql,nopgx \
-ldflags= "-s -w -extldflags '-static'" \
-trimpath
Faster Builds
Enable parallel compilation:
GO_BUILD_PARALLEL = 8 go build
Use build cache:
# Cache is automatic, but can be cleared:
go clean -cache
Dockerfile Build
Multi-stage build for minimal image:
# Build stage
FROM golang:1.25-alpine AS builder
RUN apk add --no-cache git
WORKDIR /build
# Copy main.go with plugin imports
COPY main.go .
RUN go mod init caddy-custom && \
go get github.com/caddyserver/caddy/[email protected]
RUN CGO_ENABLED=0 go build \
-tags=nobadger,nomysql,nopgx \
-ldflags= "-s -w" \
-trimpath \
-o caddy
# Runtime stage
FROM scratch
COPY --from=builder /build/caddy /usr/bin/caddy
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 80 443 2019
ENTRYPOINT [ "/usr/bin/caddy" ]
CMD [ "run" , "--config" , "/etc/caddy/Caddyfile" ]
Automated Build Pipeline
GitHub Actions Example
name : Build Custom Caddy
on :
push :
tags :
- 'v*'
jobs :
build :
runs-on : ubuntu-latest
steps :
- uses : actions/checkout@v4
- uses : actions/setup-go@v5
with :
go-version : '1.25'
- name : Install xcaddy
run : go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
- name : Build
run : |
xcaddy build v2.8.0 \
--with github.com/caddy-dns/cloudflare \
--output caddy-linux-amd64
- name : Upload artifact
uses : actions/upload-artifact@v4
with :
name : caddy-linux-amd64
path : caddy-linux-amd64
Verification
After building, verify your custom binary:
# Check version
./caddy version
# List all modules (verify plugins are included)
./caddy list-modules
# Search for specific plugin
./caddy list-modules | grep cloudflare
# Validate configuration
./caddy validate --config Caddyfile
# Test run
./caddy run --config Caddyfile
Distribution
Create Release Archive
tar -czf caddy-custom-v1.0.0-linux-amd64.tar.gz caddy
Generate Checksums
sha256sum caddy-custom-v1.0.0-linux-amd64.tar.gz > checksums.txt
Sign Release
gpg --armor --detach-sign caddy-custom-v1.0.0-linux-amd64.tar.gz
Maintenance
Update Caddy Version
Update Plugins
go get -u github.com/caddy-dns/cloudflare
go mod tidy
go build
Security Updates
# Update all dependencies
go get -u all
go mod tidy
# Verify no vulnerabilities
go list -json -m all | nancy sleuth
Best Practices
Version everything : Pin specific versions of Caddy and all plugins in production.
Test thoroughly : Run your test suite against custom builds before deploying.
Document plugins : Maintain a list of included plugins and their versions.
Automate builds : Use CI/CD to build consistently across environments.
Keep builds minimal : Only include plugins you actually use.
Next Steps
xcaddy Tool Master xcaddy for easier custom builds
Module Development Create your own custom modules
Testing Guide Test your custom builds thoroughly
Contributing Share your build configurations