A modern, secure Git repository mirroring and management platform designed for enterprise environments. GitDuppy provides automated repository synchronization, access control, audit logging, and webhook integrations while maintaining security and compliance standards.
- Automated Git repository mirroring with configurable schedules
- Full GitHub Metadata Mirroring (Issues, PRs, Releases, and Wikis)
- GitHub-like web repository browser — browse files, commits, diffs in-browser
- Role-based access control (RBAC) with fine-grained permissions
- Comprehensive audit logging for all operations
- Webhook notifications for repository events
- Built-in health monitoring and alerting
- Docker-first deployment with Kubernetes support
- End-to-end encryption for sensitive data
- OAuth2 authentication support
- REST API for programmatic management
Dashboard — live sync timeline, aggregate stats, and recent jobs across all mirrored repositories.
| Repositories | In‑browser Git browser |
|---|---|
![]() |
![]() |
| Commit history | Commit diff |
![]() |
![]() |
| Settings | Sign in |
![]() |
![]() |
The easiest way to get started is using Docker Compose:
# Clone the repository
git clone https://github.com/yourorg/gitduppy.git
cd gitduppy
# Copy the example configuration file
cp config.example.yaml config.yaml
# Start the services
docker compose up -d --build
# Access the dashboard at http://localhost:7659/login (or http://localhost:8080 inside the container)On the first run (when no users exist yet), GitDuppy seeds a single administrator account:
- Username:
admin - Email:
admin@gitmirrors.local
There is no universal default password, and the initial password is never written to the application logs. Establish it one of two ways:
- Operator-provided secret (recommended): set the
GITMIRRORS_BOOTSTRAP_ADMIN_PASSWORDenvironment variable before the first start. That value becomes the initial admin password, and you already know it out-of-band. - Auto-generated secret: if the variable is unset, GitDuppy generates a strong random password but does not display it. To retrieve it for the very first login, opt in explicitly by also setting
GITMIRRORS_BOOTSTRAP_SHOW_PASSWORD=true, which prints the generated password once at startup. Leave this unset in normal operation.
Important
Prefer supplying GITMIRRORS_BOOTSTRAP_ADMIN_PASSWORD over the one-time display. Log in with the initial password and change it immediately via the change-password flow. The password is stored only as a bcrypt hash, never in plaintext.
GitDuppy can be configured via a YAML configuration file (config.yaml) or by environment variables.
All environment variables are prefixed with GITMIRRORS_ and map to the YAML structure using underscores instead of dots (e.g., security.master_key maps to GITMIRRORS_SECURITY_MASTER_KEY).
To secure your installation, you must generate three secrets for credential encryption, session signing, and CSRF protection — each a 256-bit key. The Session Secret and CSRF Key must be exactly 32 characters long; the Master Key must be either a 32-character string or a 64-character hex-encoded value (which decodes to 32 bytes):
- Master Key (
GITMIRRORS_SECURITY_MASTER_KEY/security.master_key): the AES-256 key used to encrypt repository credentials in the database. Accepts either a 32-character string or a 64-character hex-encoded key (e.g.openssl rand -hex 32). - Session Secret (
GITMIRRORS_SECURITY_SESSION_SECRET/security.session_secret): a 32-character secret used to sign session cookies. - CSRF Key (
GITMIRRORS_SECURITY_CSRF_KEY/security.csrf_key): a 32-character key used for CSRF token generation.
Generate a 32-character secret (works for all three keys) with:
- Bash:
openssl rand -hex 16 - PowerShell:
-join ((1..16 | % { '{0:x2}' -f (Get-Random -Min 0 -Max 256) }))
Warning
openssl rand -hex 32 produces 64 characters. That is accepted for the master key (decoded from hex), but the session and CSRF keys must be exactly 32 characters — use openssl rand -hex 16 for those.
GitDuppy supports GitHub, GitLab, and Google OAuth2 for user authentication. These can be configured in your config.yaml or via env variables:
- GitHub OAuth & App Integration:
GITMIRRORS_OAUTH_GITHUB_CLIENT_ID/oauth.github.client_idGITMIRRORS_OAUTH_GITHUB_CLIENT_SECRET/oauth.github.client_secretGITMIRRORS_OAUTH_GITHUB_REDIRECT_URL(e.g.,http://localhost:7659/api/v1/oauth/github/callback)- Default Scopes:
read:user,user:email,repo(thereposcope is required for auto-discovering and mirroring private repositories). - Status Panel: The Configuration page features a live GitHub Integration Status card displaying App setup state, user connection state, active scopes, and a Sync Repositories Now button.
- GitLab OAuth:
GITMIRRORS_OAUTH_GITLAB_CLIENT_ID/oauth.gitlab.client_idGITMIRRORS_OAUTH_GITLAB_CLIENT_SECRET/oauth.gitlab.client_secretGITMIRRORS_OAUTH_GITLAB_REDIRECT_URL
- Google OAuth:
GITMIRRORS_OAUTH_GOOGLE_CLIENT_ID/oauth.google.client_idGITMIRRORS_OAUTH_GOOGLE_CLIENT_SECRET/oauth.google.client_secretGITMIRRORS_OAUTH_GOOGLE_REDIRECT_URL
Reference the provided config.example.yaml file for complete configuration options. Create a config.yaml file in the root directory or specify its path with the CONFIG_FILE environment variable.
Build and run the server binary:
go build -o gitduppy ./cmd/server
./gitduppySee the full API documentation in docs/api-reference.md.
For production deployments, use the provided Docker Compose files:
- Development:
docker-compose.yml - Production:
docker-compose.prod.ymlwith Caddy reverse proxy
The Caddy configuration is located at deployments/caddy/Caddyfile.
For comprehensive security guidelines, see docs/security.md.
GitDuppy includes a full web interface accessible at http://localhost:7659 after login.
| Page | URL | Description |
|---|---|---|
| Dashboard | /dashboard |
Overview of all repositories and recent activity |
| Repository List | /repos |
Searchable card-grid of all mirrored repositories |
| Repository Browser | /repos/:id |
Browse files and folders with branch/tag switcher |
| File Viewer | /repos/:id (click file) |
View file content with syntax highlighting |
| Commit History | /repos/:id → View Commit History |
Paginated list of commits |
| Commit Detail | /repos/:id/commit/:sha |
Single commit with full line-by-line diff |
| Settings | /config |
Application configuration |
GitDuppy manages its schema in two layers that run in order at startup:
- GORM AutoMigrate owns the base tables and columns. It is derived directly
from the model structs in
internal/modelsand creates/updates tables to match them on every boot. - goose SQL migrations (embedded from
internal/database/migrations) own the constraints, indexes and data fixes layered on top — CHECK constraints,ON DELETE CASCADEforeign keys, unique/trigram/hot-path indexes, etc. They run right after AutoMigrate viagoose.Up.
Every migration is written idempotently (IF NOT EXISTS / guarded DO blocks with
pg_constraint lookups, ADD CONSTRAINT ... NOT VALID), so it is safe on both fresh
and pre-existing databases and can run on every boot. A migration failure is fatal:
the server refuses to start rather than run with a half-applied schema.
Going forward, add new integrity constraints, indexes and data backfills as a new
numbered file in internal/database/migrations/ (e.g. 0002_*.sql) rather than
relying on AutoMigrate.
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch
- Write tests for your changes
- Submit a pull request
GitDuppy is licensed under the MIT License. See LICENSE for details.
For detailed documentation, see the docs/ directory.







