diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..6946fd8 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,70 @@ +name: Docker + +on: + push: + # push events will publish a new image, so only trigger on main branch or semver tags. + branches: ["main"] + tags: ["v*"] + pull_request: + # Run the workflow on pull_request events to ensure we can still build the image. + # We only publish the image on push events (see if statements in steps below). + branches: ["main"] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +concurrency: + group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + build-and-push-image: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + id-token: write + + steps: + - uses: actions/checkout@v4 + + - name: Setup Docker buildx + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + + - name: Log into registry ${{ env.REGISTRY }} + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + if: github.event_name == 'push' + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 + with: + context: . + push: ${{ github.event_name == 'push' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + platforms: linux/amd64,linux/arm64,linux/arm/v7 + + # Sign the Docker image + - name: Install cosign + if: github.event_name == 'push' + uses: sigstore/cosign-installer@1fc5bd396d372bee37d608f955b336615edf79c8 #v3.2.0 + - name: Sign the published Docker image + if: github.event_name == 'push' + run: cosign sign --yes ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build-and-push.outputs.digest }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3ae0b35 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,32 @@ +ARG GO_VERSION=1.22 +FROM golang:${GO_VERSION} AS builder + +ARG CADDY_VERSION=2.8.4 +RUN go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest && \ + xcaddy build v${CADDY_VERSION} --with github.com/tailscale/caddy-tailscale@main --output /usr/bin/caddy + +# From https://github.com/caddyserver/caddy-docker/blob/master/2.8/alpine/Dockerfile +FROM alpine:3.20 + +RUN mkdir -p \ + /config/caddy \ + /data/caddy \ + /etc/caddy \ + /usr/share/caddy + +COPY --from=builder /usr/bin/caddy /usr/bin/caddy +COPY examples/simple.caddyfile /etc/caddy/Caddyfile + +# See https://caddyserver.com/docs/conventions#file-locations for details +ENV XDG_CONFIG_HOME /config +ENV XDG_DATA_HOME /data + +EXPOSE 80 +EXPOSE 443 +EXPOSE 443/udp +EXPOSE 2019 + +WORKDIR /srv + +CMD ["run", "--config", "/etc/caddy/Caddyfile"] +ENTRYPOINT ["caddy"] diff --git a/README.md b/README.md index a1a09d5..2bed63b 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,23 @@ TS_AUTHKEY= ./caddy run -c examples/ [examples directory]: ./examples/ +### Run with Docker + +Run the pre-built docker image with: + +```sh +docker run -it -rm ghcr.io/tailscale/caddy-tailscale +``` + +Mount a custom Caddyfile to `/etc/caddy/Caddyfile` and optionally mount a volume +to `/config` to persist the default Tailscale state directory: + +```sh +docker run -it -rm \ + -v ./custom.caddyfile:/etc/caddyCaddyfile -v ./config:config \ + ghcr.io/tailscale/caddy-tailscale +``` + ## Configuration In a [Caddyfile], use the `tailscale` [global option] to configure your Tailscale nodes.