12 KiB
Nix Deployment Guide
This guide covers deploying Gitea Mirror using Nix flakes. The Nix deployment follows the same minimal configuration philosophy as docker-compose.alt.yml - secrets are auto-generated, and everything else can be configured via the web UI.
Prerequisites
- Nix 2.4+ installed
- For NixOS module: NixOS 23.05+
Enable Flakes (Recommended)
To enable flakes permanently and avoid typing flags, add to /etc/nix/nix.conf or ~/.config/nix/nix.conf:
experimental-features = nix-command flakes
Note: If you don't enable flakes globally, add --extra-experimental-features 'nix-command flakes' to all nix commands shown below.
Quick Start (Zero Configuration!)
Run Immediately - No Setup Required
# Run directly from the flake (local)
nix run --extra-experimental-features 'nix-command flakes' .#gitea-mirror
# Or from GitHub (once published)
nix run --extra-experimental-features 'nix-command flakes' github:RayLabsHQ/gitea-mirror
# If you have flakes enabled globally, simply:
nix run .#gitea-mirror
That's it! On first run:
- Secrets (
BETTER_AUTH_SECRETandENCRYPTION_SECRET) are auto-generated - Database is automatically created and initialized
- Startup recovery and repair scripts run automatically
- Access the web UI at http://localhost:4321
Everything else (GitHub credentials, Gitea settings, mirror options) is configured through the web interface after signup.
Development Environment
# Enter development shell with all dependencies
nix develop --extra-experimental-features 'nix-command flakes'
# Or use direnv for automatic environment loading (handles flags automatically)
echo "use flake" > .envrc
direnv allow
Build and Install
# Build the package
nix build --extra-experimental-features 'nix-command flakes'
# Run the built package
./result/bin/gitea-mirror
# Install to your profile
nix profile install --extra-experimental-features 'nix-command flakes' .#gitea-mirror
What Happens on First Run?
Following the same pattern as the Docker deployment, the Nix package automatically:
- Creates data directory:
~/.local/share/gitea-mirror(or$DATA_DIR) - Generates secrets (stored securely in data directory):
BETTER_AUTH_SECRET- Session authentication (32-char hex)ENCRYPTION_SECRET- Token encryption (48-char base64)
- Initializes database: SQLite database with Drizzle migrations
- Runs startup scripts:
- Environment configuration loader
- Crash recovery for interrupted jobs
- Repository status repair
- Starts the application with graceful shutdown handling
NixOS Module - Minimal Deployment
Simplest Possible Configuration
Add to your NixOS configuration (/etc/nixos/configuration.nix):
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
gitea-mirror.url = "github:RayLabsHQ/gitea-mirror";
};
outputs = { nixpkgs, gitea-mirror, ... }: {
nixosConfigurations.your-hostname = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
gitea-mirror.nixosModules.default
{
# That's it! Just enable the service
services.gitea-mirror.enable = true;
}
];
};
};
}
Apply with:
sudo nixos-rebuild switch
Access at http://localhost:4321, sign up (first user is admin), and configure everything via the web UI.
Production Configuration
For production with custom domain and firewall:
{
services.gitea-mirror = {
enable = true;
host = "0.0.0.0";
port = 4321;
betterAuthUrl = "https://mirror.example.com";
betterAuthTrustedOrigins = "https://mirror.example.com";
openFirewall = true;
};
# Optional: Use with nginx reverse proxy
services.nginx = {
enable = true;
virtualHosts."mirror.example.com" = {
locations."/" = {
proxyPass = "http://127.0.0.1:4321";
proxyWebsockets = true;
};
enableACME = true;
forceSSL = true;
};
};
}
Advanced: Manual Secret Management
If you prefer to manage secrets manually (e.g., with sops-nix or agenix):
- Create a secrets file:
# /var/lib/gitea-mirror/secrets.env
BETTER_AUTH_SECRET=your-32-character-minimum-secret-key-here
ENCRYPTION_SECRET=your-encryption-secret-here
- Reference it in your configuration:
{
services.gitea-mirror = {
enable = true;
environmentFile = "/var/lib/gitea-mirror/secrets.env";
};
}
Full Configuration Options
{
services.gitea-mirror = {
enable = true;
package = gitea-mirror.packages.x86_64-linux.default; # Override package
dataDir = "/var/lib/gitea-mirror";
user = "gitea-mirror";
group = "gitea-mirror";
host = "0.0.0.0";
port = 4321;
betterAuthUrl = "https://mirror.example.com";
betterAuthTrustedOrigins = "https://mirror.example.com";
# Concurrency controls (match docker-compose.alt.yml)
mirrorIssueConcurrency = 3; # Set to 1 for perfect chronological order
mirrorPullRequestConcurrency = 5; # Set to 1 for perfect chronological order
environmentFile = null; # Optional secrets file
openFirewall = true;
};
}
Service Management (NixOS)
# Start the service
sudo systemctl start gitea-mirror
# Stop the service
sudo systemctl stop gitea-mirror
# Restart the service
sudo systemctl restart gitea-mirror
# Check status
sudo systemctl status gitea-mirror
# View logs
sudo journalctl -u gitea-mirror -f
# Health check
curl http://localhost:4321/api/health
Environment Variables
All variables from docker-compose.alt.yml are supported:
# === AUTO-GENERATED (Don't set unless you want specific values) ===
BETTER_AUTH_SECRET # Auto-generated, stored in data dir
ENCRYPTION_SECRET # Auto-generated, stored in data dir
# === CORE SETTINGS (Have good defaults) ===
DATA_DIR="$HOME/.local/share/gitea-mirror"
DATABASE_URL="file:$DATA_DIR/gitea-mirror.db"
HOST="0.0.0.0"
PORT="4321"
NODE_ENV="production"
# === BETTER AUTH (Override for custom domains) ===
BETTER_AUTH_URL="http://localhost:4321"
BETTER_AUTH_TRUSTED_ORIGINS="http://localhost:4321"
PUBLIC_BETTER_AUTH_URL="http://localhost:4321"
# === CONCURRENCY CONTROLS ===
MIRROR_ISSUE_CONCURRENCY=3 # Default: 3 (set to 1 for perfect order)
MIRROR_PULL_REQUEST_CONCURRENCY=5 # Default: 5 (set to 1 for perfect order)
# === CONFIGURE VIA WEB UI (Not needed at startup) ===
# GitHub credentials, Gitea settings, mirror options, scheduling, etc.
# All configured after signup through the web interface
Database Management
The Nix package includes a database management helper:
# Initialize database (done automatically on first run)
gitea-mirror-db init
# Check database health
gitea-mirror-db check
# Fix database issues
gitea-mirror-db fix
# Reset users
gitea-mirror-db reset-users
Home Manager Integration
For single-user deployments:
{ config, pkgs, ... }:
let
gitea-mirror = (import (fetchTarball "https://github.com/RayLabsHQ/gitea-mirror/archive/main.tar.gz")).packages.${pkgs.system}.default;
in {
home.packages = [ gitea-mirror ];
# Optional: Run as user service
systemd.user.services.gitea-mirror = {
Unit = {
Description = "Gitea Mirror Service";
After = [ "network.target" ];
};
Service = {
Type = "simple";
ExecStart = "${gitea-mirror}/bin/gitea-mirror";
Restart = "always";
Environment = [
"DATA_DIR=%h/.local/share/gitea-mirror"
"HOST=127.0.0.1"
"PORT=4321"
];
};
Install = {
WantedBy = [ "default.target" ];
};
};
}
Docker Image from Nix (Optional)
You can also use Nix to create a Docker image:
# Add to flake.nix packages section
dockerImage = pkgs.dockerTools.buildLayeredImage {
name = "gitea-mirror";
tag = "latest";
contents = [ self.packages.${system}.default pkgs.cacert pkgs.openssl ];
config = {
Cmd = [ "${self.packages.${system}.default}/bin/gitea-mirror" ];
ExposedPorts = { "4321/tcp" = {}; };
Env = [
"DATA_DIR=/data"
"DATABASE_URL=file:/data/gitea-mirror.db"
];
Volumes = { "/data" = {}; };
};
};
Build and load:
nix build --extra-experimental-features 'nix-command flakes' .#dockerImage
docker load < result
docker run -p 4321:4321 -v gitea-mirror-data:/data gitea-mirror:latest
Comparison: Docker vs Nix
Both deployment methods follow the same philosophy:
| Feature | Docker Compose | Nix |
|---|---|---|
| Configuration | Minimal (only BETTER_AUTH_SECRET) | Zero config (auto-generated) |
| Secret Generation | Auto-generated & persisted | Auto-generated & persisted |
| Database Init | Automatic on first run | Automatic on first run |
| Startup Scripts | Runs recovery/repair/env-config | Runs recovery/repair/env-config |
| Graceful Shutdown | Signal handling in entrypoint | Signal handling in wrapper |
| Health Check | Docker healthcheck | systemd timer (optional) |
| Updates | docker pull |
nix flake update && nixos-rebuild |
Troubleshooting
Check Auto-Generated Secrets
# For standalone
cat ~/.local/share/gitea-mirror/.better_auth_secret
cat ~/.local/share/gitea-mirror/.encryption_secret
# For NixOS service
sudo cat /var/lib/gitea-mirror/.better_auth_secret
sudo cat /var/lib/gitea-mirror/.encryption_secret
Database Issues
# Check if database exists
ls -la ~/.local/share/gitea-mirror/gitea-mirror.db
# Reinitialize (deletes all data!)
rm ~/.local/share/gitea-mirror/gitea-mirror.db
gitea-mirror-db init
Permission Issues (NixOS)
sudo chown -R gitea-mirror:gitea-mirror /var/lib/gitea-mirror
sudo chmod 700 /var/lib/gitea-mirror
Port Already in Use
# Change port
export PORT=8080
gitea-mirror
# Or in NixOS config
services.gitea-mirror.port = 8080;
View Startup Logs
# Standalone (verbose output on console)
gitea-mirror
# NixOS service
sudo journalctl -u gitea-mirror -f --since "5 minutes ago"
Updating
Standalone Installation
# Update flake lock
nix flake update --extra-experimental-features 'nix-command flakes'
# Rebuild
nix build --extra-experimental-features 'nix-command flakes'
# Or update profile
nix profile upgrade --extra-experimental-features 'nix-command flakes' gitea-mirror
NixOS
# Update input
sudo nix flake lock --update-input gitea-mirror --extra-experimental-features 'nix-command flakes'
# Rebuild system
sudo nixos-rebuild switch --flake .#your-hostname
Migration from Docker
To migrate from Docker to Nix while keeping your data:
-
Stop Docker container:
docker-compose -f docker-compose.alt.yml down -
Copy data directory:
# For standalone cp -r ./data ~/.local/share/gitea-mirror # For NixOS sudo cp -r ./data /var/lib/gitea-mirror sudo chown -R gitea-mirror:gitea-mirror /var/lib/gitea-mirror -
Copy secrets (if you want to keep them):
# Extract from Docker volume docker run --rm -v gitea-mirror_data:/data alpine \ cat /data/.better_auth_secret > better_auth_secret docker run --rm -v gitea-mirror_data:/data alpine \ cat /data/.encryption_secret > encryption_secret # Copy to new location cp better_auth_secret ~/.local/share/gitea-mirror/.better_auth_secret cp encryption_secret ~/.local/share/gitea-mirror/.encryption_secret chmod 600 ~/.local/share/gitea-mirror/.*_secret -
Start Nix version:
gitea-mirror
CI/CD Integration
Example GitHub Actions workflow:
name: Build with Nix
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v24
with:
extra_nix_config: |
experimental-features = nix-command flakes
- uses: cachix/cachix-action@v12
with:
name: gitea-mirror
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- run: nix build
- run: nix flake check
# Note: GitHub Actions runner usually has flakes enabled by install-nix-action
Resources
- Nix Manual
- NixOS Options Search
- Nix Pills Tutorial
- Project Documentation
- Docker Deployment - Equivalent minimal config