- Use DeterminateSystems/nix-installer-action for Nix installation - Use DeterminateSystems/magic-nix-cache-action for caching (free, no setup) - Update documentation to remove Cachix references - Add nix branch to CI triggers
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 (see .github/workflows/nix-build.yml):
name: Nix Build
on: [push, pull_request]
permissions:
contents: read
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main
- run: nix flake check
- run: nix build --print-build-logs
This uses:
- Determinate Nix Installer - Fast, reliable Nix installation with flakes enabled by default
- Magic Nix Cache - Free caching using GitHub Actions cache (no account needed)
Resources
- Nix Manual
- NixOS Options Search
- Nix Pills Tutorial
- Project Documentation
- Docker Deployment - Equivalent minimal config