Nearly Free Serverless Private Git Repository
Disclaimer: This translated content is generated by a large language model.
As a developer, we always need a place to store personal code that is not convenient to make public. GitHub's private repositories are certainly good, but they always lack the feeling of "being in control," and self-hosting Gitea or GitLab is too heavy, requiring a virtual machine to run a webpage long-term. Recently, I explored a cost-saving solution: leveraging the free tier of cloud providers' Serverless offerings, combined with Alpine Linux + Dropbear, to build an image that automatically shuts down when idle (0 cost), creating a private Git server that can scale on demand.
This solution uses the SSH protocol to access a private Git repository and runs the Git service using a container image. It only processes requests when SSH receives them, thereby fully utilizing the cloud provider's idle shutdown feature.
Solution Highlights
- Extremely low cost: Compute resources utilize the monthly free tiers of Azure or other cloud providers (e.g., Google Run); storage uses Azure Files, which consumes very little for repositories containing almost only source code.
- Data security: Data is used as persistent storage, so data is not lost if the container crashes.
- Automatic scaling: Cloud providers often offer a Scale to Zero feature, where the number of replicas is 0 when not in use (no charge); connect via SSH, and it wakes up instantly.
Technology Selection: Why Alpine + Dropbear?
When building this image, I did not choose the common Ubuntu + OpenSSH combination but opted for Alpine + Dropbear instead. This was not for showing off, but a calculated decision based on resource saving in a Serverless scenario.
Alpine Linux is the first choice for cold starts and embedded systems. When Azure Container Apps is configured to scale to 0, if you initiate a request, the platform needs to spin up the container from scratch, which is the "cold start" process. Ubuntu's base image is usually 70MB+, while Alpine is only 5MB. A smaller size means faster image pull and decompression speeds. In a cold start scenario, Alpine's minimalist initialization process can save us a few seconds of waiting time.
Dropbear: Embedded Soul OpenSSH is the industry standard, but for an extremely lightweight micro-container, it appears too "bloated." Dropbear is an SSH server specifically designed for embedded systems (such as OpenWrt routers). Its memory footprint is extremely low (usually only a few MB), while OpenSSH's per-connection process consumption is much higher. Dropbear's server and key tools are contained within a tiny binary, reducing syscall overhead. This container does not require complex PAM authentication, GSSAPI, or X11 forwarding. It only needs a secure tunnel capable of running git-upload-pack, and Dropbear provides exactly what is needed.
Summary: The choice of Alpine + Dropbear is to achieve good response speed even under the lowest resource constraints, while reserving memory for Git's garbage collection mechanism to prevent OOM (Out Of Memory). The packaged image size is around 10MB and can run normally with 100 MB or even less memory. This image is also very suitable for running on internal home routers or simple embedded devices, serving as an almost burden-free Git repository.
Container Image Configuration
I have published an image on GitHub's container registry; the current version is 1.1.4, and the source code is published in a Cloudflare R2 storage bucket. sha256sum:4670df2ee8f921a3c8414f5c0e58f8d7262a9e6d8730597d887c1035adf9383e
Image address: ghcr.io/kwfcfc/git-server:latest
Below, I will introduce how to use this image with a docker-compose file and a .env environment configuration file.
name: simple-git
services:
simple-git:
image: ghcr.io/kwfcfc/git-server:latest
container_name: git-test
ports:
- "2222:22" # Map to your desired port
volumes:
- git_repo:/home/git
env_file: ".env"
volumes:
git_repo: This is the environment variable file.
# Base64 encoded ed25519 key
ED25519_KEY=
# Base64 encoded rsa key
RSA_KEY=
# you can also mount the authorized_keys into
# /auth/authorized_keys file inside container
AUTHORIZED_KEYS='ssh-ed25519 change-me'
REPO_NAMES='repo1 repo2.git'
GIT_PORT=22 When starting this image, you need to pass a Base64 encoded ED25519 and RSA private key, so that local errors are not caused by key changes each time the container restarts. This private key format is different from OpenSSH, so you need to use Dropbear's tools for conversion. If you have the Dropbear program and Base64 encoding tools locally, you can also generate them using the following commands:
dropbearkey -t ed25519 -f dropbear_ed25519_host_key
dropbearkey -t rsa -f dropbear_rsa_host_key
base64 -i dropbear_ed25519_host_key
base64 -i dropbear_rsa_host_key The AUTHORIZED_KEYS in the environment variables are your local SSH public keys, typically ~/.ssh/id_ed25519.pub or ~/.ssh/id_rsa.pub. If multiple public keys are needed, you can write them one per line into a file and mount it to /auth/authorized_keys. REPO_NAMES are the names of the repositories you want to create on this server: when starting, the container will create these repositories under /home/git/. Finally, GIT_PORT is the default port opened by Dropbear. If you are using docker-compose or certain cloud providers, you can map it to other ports.
Connection Method
If your Git server is running successfully, you can test it with the following command (replace GIT_PORT, id_ed25519, and your_ip with your configuration):
ssh -T -p GIT_PORT -i ~/.ssh/id_ed25519 git@your.ip If configured successfully, you should see:
fatal: Interactive git shell is not enabled.
hint: ~/git-shell-commands should exist and have read and execute access. Cloud Provider Configuration and Deployment Process (Taking Azure Container Apps as an Example)
When deploying, several key settings determine its success.
Mount Storage (Volumes): Create an Azure File Share. Mount it in Container Apps with the path set to
/home/git. This ensures your code repository is persistent.Networking (Ingress): You need to create a network interface first, then let the container app use this address.
Traffic: TCP
Target Port: 22
Exposed Port: 22 (or other; my recommendation is to choose an uncommon high-numbered port to avoid being)
Ultimate Cost Saving: Scale to Zero
This is the essence of Serverless. We need to set rules: power on when someone connects via SSH, power off when no one is connected.
Frequently Asked Questions (Q&A)
Q: There's always an IP like 100.100.x.x connecting in the logs and immediately disconnecting. Is it a hacker?
A: No. This is Azure Ingress's health check probe. Because we enabled TCP Ingress, the load balancer needs to confirm that the port is open. You can ignore these "Exit before auth" logs.
Q: How long does a cold start take?
A: Approximately 15-30 seconds. When you push code for the first time in the morning, you might get stuck on this interface for a while, but once the container is pulled up, subsequent operations will be very fast.
Q: Is it really free every month?
A: Compute costs: The free tier includes 180,000 vCPU-seconds. For a 0.25 vCPU specification, this is equivalent to 200 hours of active usage per month. For a personal repository, this is almost never fully used. As for storage costs, Azure Files is pay-as-you-go. Storing 1GB of code costs approximately $0.06 (about 0.4 yuan) per month.
Summary
With this solution, for less than the cost of a cup of coffee, we have a highly available, automatically backed-up, globally accessible private Git repository on Azure. Additionally, services like Google Cloud Run or other cloud providers offer substantial free tiers, allowing this image to run at a very low or even free cost. You can also try using such a simple Git repository on your own embedded devices.
评论系统尚未配置。请在 .env 中填写 giscus 所需的环境变量。