Easily Build Your Private Git Server With Caddy and Containers (Git Over HTTPS)
AI generated + manually edited, some basic knowledge about containers (Docker/Podman) is omitted in this article
Want to have a private Git server accessible via HTTPS like GitHub, but don’t want to deploy complex systems like GitLab or Gitea? This article will show you how to use Caddy2 and Docker to quickly set up your own Git service in a lightweight, efficient, and secure way within minutes.
Final Result
- Lightweight and Efficient: Based on Caddy and native Git, with extremely low resource usage.
- Automatic HTTPS: Caddy automatically manages TLS certificates, all transmissions are encrypted.
- Simple Authentication: Uses Caddy’s built-in
basic_auth
for username/password protection. - Data Persistence: Git repositories and Caddy certificates are persisted through Docker Volumes, ensuring data security.
- Easy Maintenance: All components are packaged in containers, one-click start/stop, easy migration.
Prerequisites
Before starting, please ensure you have:
- A server with
Podman
(orDocker
andDocker Compose
) installed. - A domain name pointing to your server IP (e.g.,
git.your-domain.com
). - Server ports 80 and 443 are open for Caddy to request certificates and provide HTTPS service.
Directory Structure
For easy management, let’s first create the following directory and file structure:
/my-caddy-git-server
├── Caddyfile
├── compose.yml
├── custom-containerfile/
│ └── Containerfile
├── git-repos/
│ └── (Git repositories will be stored here)
└── caddy_data/
└── (Caddy certificates and state will be stored here)
Step 1: Build Caddy Image with caddy-cgi
Plugin
We need a custom Caddy image that includes the caddy-cgi
plugin so Caddy can execute the git-http-backend
program.
Write the following content in custom-containerfile/Containerfile
:
# Stage 1: Builder - Only used to compile Caddy with plugins
FROM docker.io/library/caddy:2-builder AS builder
# Use xcaddy to build Caddy with cgi plugin
# caddy-cgi plugin allows Caddy to act as a CGI gateway
RUN xcaddy build \
--with github.com/aksdb/caddy-cgi/v2
# Stage 2: Final runtime image
FROM docker.io/library/caddy:latest
# Install git-daemon in the final image so git-http-backend is available in runtime
RUN apk update && apk add --no-cache git git-daemon
# Copy the compiled Caddy binary with plugins from builder stage
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
This Containerfile uses multi-stage builds, with the final image containing only the necessary git-daemon
and Caddy with plugins, very streamlined.
Step 2: Configure Caddyfile
Caddyfile
is Caddy’s configuration file, we use it to define Git service behavior.
Write the following content in Caddyfile
in the project root directory:
# Your Git service domain
git.your-domain.com {
# Enable basic authentication to protect the entire service
basic_auth {
# Use `caddy hash-password` command to generate your password hash
# Replace "your_username" with your username
# Replace "$2a$14$..." with the generated hash password
your_username $2a$14$ABC...
}
# Route all requests to git-http-backend
# In Alpine images, git-http-backend path is usually this
cgi * /usr/libexec/git-core/git-http-backend {
# Key: Set environment variables to tell Git backend how to work
# GIT_PROJECT_ROOT: Root directory of Git repositories, must match mount path in compose.yml
# GIT_HTTP_EXPORT_ALL: Allow clients to discover all repositories without extra configuration
env GIT_PROJECT_ROOT=/git-repos GIT_HTTP_EXPORT_ALL=1
}
}
Step 3: Write Compose File
The compose.yml
file connects all parts (custom image, configuration files, data volumes) together.
Write the following content in compose.yml
in the project root directory:
name: git-server
services:
git-server:
# Specify using our custom Containerfile for building
build: ./custom-containerfile
restart: unless-stopped
ports:
# Map HTTPS and HTTP ports
- "443:443"
- "80:80"
volumes:
# Mount Caddyfile
- ./Caddyfile:/etc/caddy/Caddyfile
# Mount directory for storing Git repositories
- ./git-repos:/git-repos
# Mount Caddy data volume for persisting TLS certificates
- ./caddy_data:/data
Step 4: Start Service and Create Repositories
-
Start Service In the
my-caddy-git-server
directory, run the following command to start the service:podman-compose up -d
Compose will automatically build the image, create and start the container. Caddy will request and configure HTTPS certificates for your domain.
-
Create Your First Repository After the service starts, create your first bare repository in the host’s
git-repos
directory.# Enter the repository storage directory cd git-repos # Create a bare repository named my-project.git git init --bare my-project.git
-
(Optional) Migrate Existing Repository If you want to migrate a repository from GitHub or elsewhere, you can use the
--mirror
option.# This will create a mirrored bare repository my-awesome-project.git in current directory git clone --mirror https://github.com/someuser/my-awesome-project.git
Step 5: Clone and Push
Everything is ready! Now you can access your Git repositories via HTTPS from any machine. Clone URL format is https://<your-domain>/<repository-name>.git
.
Clone repository:
git clone https://git.your-domain.com/my-project.git
Git will prompt you for the username and password configured in Caddyfile
. After authentication, the repository will be successfully cloned.
Push updates:
cd my-project
echo "Hello, private Git server!" > README.md
git add .
git commit -m "Initial commit"
git push origin main
Similarly, after entering credentials, your code will be securely pushed to the private server.
Summary
Using Caddy and containers, we implemented a fully functional, secure, and reliable private Git server with minimal configuration. Compared to GitLab/Gitea solutions, it has extremely low resource usage, low maintenance costs, and enjoys the convenience of automatic HTTPS from Caddy. I hope this tutorial helps you easily control your own code.
Appendix: About Git Over SSH
This article focuses on Git over HTTPS. If you prefer using SSH, you actually don’t need Caddy or containers.
You just need a standard user account on your server with Git installed. Then:
- Create a bare repository on the server (
git init --bare my-repo.git
). - Ensure your SSH public key is in the server user’s
~/.ssh/authorized_keys
file. - Use SSH protocol for cloning:
git clone user@your-domain.com:/path/to/my-repo.git
.
This approach relies on the system’s SSH service and is completely independent from the HTTPS service described in this article.