caddy-docker is the template that produces the official Caddy image
Source for the official Caddy v2 Docker Image
At a glance
- What is it?
- caddy-docker is the source repository for the official caddy Docker image, where the Dockerfiles are generated from gomplate templates and a stackbrew-config.yaml, then published to Docker Hub and the official-images library. It is build infrastructure, not an application, and the README says so in one line.
- Who is it for?
- Adopt caddy-docker if you need to build or customise the official Caddy image, for example to pin a version, add a build plugin, or reproduce a published image locally. Do not adopt it to learn how to run Caddy, because the README points all documentation to Docker Hub, and do not expect a stable build interface, since the release process is described as semi-automated and held together with shell scripts and gomplate.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 20 days ago.
- What is it written in?
- Mainly Dockerfile, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The official image, and where its documentation actually lives
caddy-docker is the repo where the official caddy Docker image sources live, and the repository description says the same thing: source for the official Caddy v2 Docker Image. The primary language of the repository is listed as Dockerfile, which fits, since the Dockerfiles are the artefact.
The README is short and points elsewhere for everything a user would want. It says to see the Docker Hub page at hub.docker.com/_/caddy for documentation, and the homepage field points at that same page. The README also links the image itself and the Docker Build workflow badge on the master branch.
So the division of labour is clear. Docker Hub holds the user facing documentation, the tags, and the image. This repository holds the inputs that produce it. That is worth understanding before you start, because a person who opens this repository expecting a server configuration will not find one, and a person who opens it expecting to see how the image is built will find templates rather than finished files.
There are two places to take an issue. For a problem with the Docker image itself, the README asks you to open an issue in this repository. For a suggested update to the image documentation, it points at the docker-library/docs repository, which is where the text on Docker Hub comes from.
The licence is Apache 2.0 for the image sources, and the README links the licence information for the software contained in the image, which is the Caddy licence in the caddy repository.
The Dockerfiles are generated, not written by hand
The top level of the repository is a template engine setup rather than a set of finished files.
There is a Dockerfile.tmpl for the main image, Dockerfile.builder.tmpl for the builder variant, Dockerfile.windows.tmpl and Dockerfile.windows-builder.tmpl for the Windows images, and Dockerfile.nanoserver.tmpl for the nanoserver target. Alongside them are render-dockerfiles.tmpl, which is the template that stitches them together, caddy-builder.sh.tmpl for the builder script, and stackbrew.tmpl plus stackbrew-config.yaml for the version metadata. There is also a 2.11/ directory, which appears to hold a base Dockerfile for a specific version line.
The Makefile shows the whole pipeline. The default goal is gen-dockerfiles, and that target runs gomplate with each template passed in as a named variable, the checksums fetched by a plugin script, and stackbrew-config.yaml supplied as the config.
gen-dockerfiles: render-dockerfiles.tmpl Dockerfile.tmpl Dockerfile.builder.tmpl Dockerfile.windows.tmpl Dockerfile.windows-builder.tmpl Dockerfile.nanoserver.tmpl */*/Dockerfile.base
@gomplate \
--plugin getChecksums=./getChecksums.sh \
-t dockerfile=Dockerfile.tmpl \
-t builder-dockerfile=Dockerfile.builder.tmpl \
-t windows-dockerfile=Dockerfile.windows.tmpl \
-t windows-builder-dockerfile=Dockerfile.windows-builder.tmpl \
-t nanoserver-dockerfile=Dockerfile.nanoserver.tmpl \
-t caddy-builder=caddy-builder.sh.tmpl \
-c config=./stackbrew-config.yaml \
-f $<The pattern is worth reading carefully. The target depends on the render template, the five per-target templates, and a wildcard for the base Dockerfiles found in the version directories. gomplate is then invoked with a plugin, getChecksums, that is a local shell script, which is how the release pipeline verifies the Caddy binaries it downloads.
The result is that no Dockerfile in this repository is edited directly. They are generated into the version directories from the templates and the version configuration, and the generated files are what get committed.
Where the version number lives, and how checksums get in
The single source of truth for the Caddy version is stackbrew-config.yaml, and the release instructions say to update the caddy_version value in that file and save.
That is the whole configuration surface for a version bump, which is a good design for a repository whose job is to track an upstream release. Everything else, the download URLs, the checksums, the tags, is derived from it.
The checksums arrive through a gomplate plugin, and the plugin is a shell script in the repository, getChecksums.sh, which is invoked during the Dockerfiles generation step. There is also fileCommit.sh, used by the library/caddy target as a plugin, and it appears to record the commit of the image contents.
The second target, library/caddy, generates the file that the official-images library consumes.
library/caddy: stackbrew.tmpl stackbrew-config.yaml gen-dockerfiles
@gomplate \
--plugin fileCommit=./fileCommit.sh \
-c config=./stackbrew-config.yaml \
-f $< \
-o $@
@touch $@There is a third target, .github/dependabot.yml, generated from a template of the same name and dependent on the same find over Dockerfile.base files, so the Dependabot configuration follows the base image versions automatically rather than being maintained by hand.
The Makefile also carries .PHONY, .DELETE_ON_ERROR and .SECONDARY, and the last one matters practically. Without .SECONDARY, make would delete the intermediate generated files, and the repository needs the generated Dockerfiles committed. The .DELETE_ON_ERROR guard means a failed render does not leave a half written Dockerfile behind, which is the behaviour you want in a pipeline that runs in CI.
The release process, described as semi-automated
The README describes the release process for maintainers with a blunt phrase: it is currently semi-automated, held together with shell scripts and gomplate, and duct tape. That is the maintainers telling you what the state of the tooling is, which is more useful than a claim that it is fully automated.
The six steps are worth reading as a whole.
One, update the stackbrew-config.yaml file, updating caddy_version, and save. Two, run make, noting that you will need gomplate on your path. Three, commit all changed Dockerfiles and stackbrew-config.yaml and issue a PR. Four, once the CI passes and the PR is merged, pull and run make all, which should update the library/caddy file. Five, commit the updated library/caddy file and push directly to master. Six, finally, issue a PR for docker-library/official-images/library/caddy containing the updated library/caddy file.
Step five is the odd one, pushing straight to master while everything else goes through a pull request. It exists because the library/caddy file has to be in the repository before the official-images pull request in step six can reference it, and the maintainer is not going to open two pull requests for one version bump.
The make target for step two is plain make, and the default goal is gen-dockerfiles, so a bare make renders the Dockerfiles. The make all target does the three things: gen-dockerfiles, library/caddy, and the dependabot file.
For an outside contributor, steps one through three are the relevant ones. Everything past that is maintainer territory.
What a custom image is for, and where the boundary is
There is a legitimate reason to build from this repository rather than pulling the published image, and it is worth being precise about it.
The published image on Docker Hub is the supported artefact. The README directs every user to it and offers no alternative. Building here is for people who need something the published tag does not give them, such as a version that has not been published yet, a build with an extra plugin compiled in, a variant for an architecture the published tags do not cover, or a need to reproduce or audit a published image.
The boundary is that this repository produces images, and the documentation for using them is elsewhere. The image documentation itself lives in the docker-library/docs repository, and the README points there for changes to it.
There is also a licensing nuance in the layout of the project. The Docker Hub entry is the caddy official image, and the README links the licence information for the software contained in the image, which is in the caddy repository, not here. So there are two artefacts with two sets of terms, the packaging in this repository under Apache 2.0 and the server software in the other. Anyone building a customised image and redistributing it needs to satisfy both, and the README's pointer to the caddy licence is the place to start.
The Windows and nanoserver targets are the clearest example of why a template approach is needed. One Caddy version has to produce a Linux image, a Linux builder image, a Windows image, a Windows builder image, and a nanoserver image, and keeping five hand-maintained Dockerfiles in step with one version number is exactly the job a generator is for.
Maintenance, and what the absence of releases means
The repository is not archived and the last push was on 2026-10-03. There are no GitHub releases, and that is not an omission, it is the design. This repository does not release anything to users. The releases that matter are the image tags on Docker Hub, and they are produced by the release process above and then propagated to the official-images library.
So the git history here is the history of the image definition, not the history of a versioned artefact. When you want to know which Caddy version is in a given image, the answer is in stackbrew-config.yaml at the commit that produced it, or in the library/caddy file, or on the Docker Hub page. When you want to know what changed in the image between two tags, the git log is where you look, and it is a log of template and config changes rather than of changelog entries.
The Dockerfile Build workflow runs on the master branch and its badge is in the README, so the state of the pipeline is visible at a glance. There is a .github/ directory, a hooks/ directory, and a .gitignore, and the generated dependabot configuration is produced by the Makefile rather than edited by hand, which is what keeps the dependency updates for the base images in step with the template changes.
One structural note for anyone reading the tree. The 2.11/ directory at the top level holds a Dockerfile.base, which the gen-dockerfiles target picks up through its wildcard. The presence of a version-named directory with a base Dockerfile alongside the templates is how a per-version override is expressed, and it is also why the target list includes that wildcard rather than naming directories one by one.
Editorial conclusion
Adopt caddy-docker if you need to build or customise the official Caddy image, for example to pin a version, add a build plugin, or reproduce a published image locally. Do not adopt it to learn how to run Caddy, because the README points all documentation to Docker Hub, and do not expect a stable build interface, since the release process is described as semi-automated and held together with shell scripts and gomplate. Before you build, read the version pin in stackbrew-config.yaml, install gomplate as the Makefile requires, and check the library/caddy file if you care about what ends up in the official-images library.
Frequently asked questions
What is the caddy-docker repository?
It is the repo where the official caddy Docker image sources live, covering the Linux, builder, Windows, Windows builder and nanoserver images. The README directs all user documentation to https://hub.docker.com/_/caddy.
How do I build the Caddy Docker image?
You need gomplate on your path, then run make, whose default target is gen-dockerfiles. The Makefile passes each Dockerfile template to gomplate, fetches checksums through the local getChecksums.sh plugin, and uses stackbrew-config.yaml as the config.
Where is the Caddy version specified?
In stackbrew-config.yaml, by updating the caddy_version value. The release instructions say to update that file and save, and everything else, the download URLs, the checksums and the tags, is derived from it.
How do I report a problem with the Caddy Docker image?
For an issue or suggestion about the image, open an issue in the caddy-docker repository. For a suggested update to the image documentation, the README points to the docker-library/docs repository, which holds the text shown on Docker Hub.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/caddyserver-caddy-docker)Was this useful?