Add comprehensive backup system for Gitea database

This commit is contained in:
2025-03-01 23:28:11 -07:00
parent dcc2dda769
commit 5084a0c98f
7 changed files with 385 additions and 1 deletions

73
BACKUP-README.md Normal file
View File

@@ -0,0 +1,73 @@
# Gitea Backup Strategy
This document outlines the backup strategy for your Gitea installation, focusing on ensuring your database is always backed up in at least one place.
## Backup Methods
This setup provides two complementary backup methods:
### 1. PostgreSQL Database Dumps
Database dumps are SQL files containing all your database data. These are the most reliable way to back up a PostgreSQL database.
- **Script**: `backup-gitea-db.ps1`
- **Output**: SQL dumps in the `backups` directory, compressed as ZIP files
- **Retention**: Keeps the last 10 backups by default
### 2. Docker Volume Backups
This method backs up the entire PostgreSQL data volume, which includes all database files.
- **Script**: `backup-volume.ps1`
- **Output**: TAR archives in the `backups` directory, compressed as ZIP files
- **Retention**: Keeps the last 5 volume backups by default
## Automated Backups
You can set up automated daily backups using the included script:
```powershell
.\schedule-backup.ps1
```
This creates a Windows Scheduled Task that runs the database backup script daily at 3 AM.
## Restoring from Backups
### Restoring from a Database Dump
```powershell
.\restore-gitea-db.ps1 -BackupFile "backups\gitea-db-backup-2025-03-01_10-30-00.sql.zip"
```
### Restoring from a Volume Backup
```powershell
.\restore-volume.ps1 -BackupFile "backups\postgres-volume-backup-2025-03-01_10-30-00.tar.zip"
```
## Best Practices
1. **Regular Backups**: Run backups at least daily
2. **Multiple Backup Methods**: Use both database dumps and volume backups
3. **Off-site Storage**: Copy your backups to an external drive or cloud storage
4. **Test Restores**: Periodically test restoring from your backups
5. **Version Control**: Keep your Gitea configuration files in version control
## Important Notes
- **Never** run `docker-compose down -v` unless you have a recent backup
- When upgrading Gitea, always create a backup first
- The database volume (`postgres-data`) persists even when containers are stopped or removed, but can be lost if explicitly deleted
## Manual Backup Commands
If you need to create a backup manually:
```powershell
# Database dump
.\backup-gitea-db.ps1
# Volume backup
.\backup-volume.ps1
```

View File

@@ -16,6 +16,7 @@ This is a Docker Compose configuration for running Gitea with PostgreSQL, config
- SSH access for Git operations
- Persistent data storage
- Self-signed SSL certificates (can be replaced with Let's Encrypt)
- Automated database backup system
## Configuration
@@ -96,6 +97,51 @@ Replace `your.domain.here` with your actual domain name.
- For production use, consider using Let's Encrypt certificates
- Self-signed certificates will show browser security warnings
## Backup System
This setup includes a comprehensive backup strategy to ensure your Gitea data is always protected. The backup system provides two complementary methods:
### Database Backups
PowerShell scripts are included to manage database backups:
1. **Creating Backups**:
```powershell
powershell -ExecutionPolicy Bypass -File .\backup-gitea-db.ps1
```
This creates a SQL dump of your PostgreSQL database, compressed as a ZIP file in the `backups` directory.
2. **Volume Backups**:
```powershell
powershell -ExecutionPolicy Bypass -File .\backup-volume.ps1
```
This backs up the entire PostgreSQL data volume as a TAR archive, compressed as a ZIP file.
3. **Automated Backups**:
```powershell
powershell -ExecutionPolicy Bypass -File .\schedule-backup.ps1
```
This creates a Windows Scheduled Task that runs database backups daily at 3 AM.
4. **Restoring from Backups**:
```powershell
# Restore from database dump
powershell -ExecutionPolicy Bypass -File .\restore-gitea-db.ps1 -BackupFile "backups\your-backup-file.sql.zip"
# Restore from volume backup
powershell -ExecutionPolicy Bypass -File .\restore-volume.ps1 -BackupFile "backups\your-volume-backup.tar.zip"
```
### Backup Best Practices
- Keep multiple backups using both methods (database dumps and volume backups)
- Store backups in multiple locations (local and off-site)
- Test restoring from backups periodically
- Create a backup before upgrading Gitea or making significant changes
- **Never** run `docker-compose down -v` unless you have a recent backup
For more detailed information about the backup system, see [BACKUP-README.md](BACKUP-README.md).
## Stopping the Services
To stop the services, run:
@@ -103,6 +149,8 @@ To stop the services, run:
docker-compose down
```
**Important**: Do not use the `-v` flag (`docker-compose down -v`) unless you intend to delete all data, as this will remove the Docker volumes containing your database.
## Data Persistence
All data is stored in Docker volumes and local directories:
@@ -112,6 +160,7 @@ All data is stored in Docker volumes and local directories:
- Docker volumes (managed by Docker):
- `gitea-data` - Gitea repositories and application data
- `postgres-data` - PostgreSQL database files
- `./backups/` - Database and volume backups
## Troubleshooting
@@ -126,4 +175,9 @@ All data is stored in Docker volumes and local directories:
3. **Local Network Access**:
- If bee8333.ddns.net doesn't resolve locally, use localhost:3000 instead
- Add an entry to your hosts file if needed
- Add an entry to your hosts file if needed
4. **Database Backup Issues**:
- Ensure Docker is running when attempting backups
- Check that the container names match those in the backup scripts
- For PowerShell execution issues, use the `-ExecutionPolicy Bypass` flag

32
backup-gitea-db.ps1 Normal file
View File

@@ -0,0 +1,32 @@
# Gitea Database Backup Script
$timestamp = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"
$backupDir = ".\backups"
$backupFile = "$backupDir\gitea-db-backup-$timestamp.sql"
# Ensure backup directory exists
if (-not (Test-Path $backupDir)) {
New-Item -ItemType Directory -Path $backupDir
}
# Create database dump
Write-Host "Creating database backup to $backupFile..."
docker exec gitea-db pg_dump -U gitea -d gitea > $backupFile
# Check if backup was successful
if ($LASTEXITCODE -eq 0 -and (Test-Path $backupFile) -and (Get-Item $backupFile).Length -gt 0) {
Write-Host "Backup completed successfully!"
# Optional: Compress the backup file
Compress-Archive -Path $backupFile -DestinationPath "$backupFile.zip" -Force
Remove-Item $backupFile
Write-Host "Backup compressed to $backupFile.zip"
} else {
Write-Host "Backup failed!" -ForegroundColor Red
}
# Optional: Clean up old backups (keep last 10)
$oldBackups = Get-ChildItem -Path $backupDir -Filter "gitea-db-backup-*.zip" | Sort-Object LastWriteTime -Descending | Select-Object -Skip 10
foreach ($backup in $oldBackups) {
Remove-Item $backup.FullName
Write-Host "Removed old backup: $($backup.Name)"
}

41
backup-volume.ps1 Normal file
View File

@@ -0,0 +1,41 @@
# Script to backup the entire Postgres Docker volume
$timestamp = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"
$backupDir = ".\backups"
$volumeName = "gitea-docker_postgres-data"
$backupFile = "$backupDir\postgres-volume-backup-$timestamp.tar"
# Ensure backup directory exists
if (-not (Test-Path $backupDir)) {
New-Item -ItemType Directory -Path $backupDir
}
# Check if volume exists
$volumeExists = docker volume ls --format "{{.Name}}" | Select-String -Pattern "^$volumeName$"
if (-not $volumeExists) {
Write-Host "Volume $volumeName not found!" -ForegroundColor Red
exit 1
}
# Create a temporary container to access the volume
Write-Host "Creating backup of Docker volume $volumeName..."
docker run --rm -v ${volumeName}:/volume -v ${PWD}/${backupDir}:/backup alpine tar -cf /backup/$(Split-Path $backupFile -Leaf) -C /volume ./
# Check if backup was successful
if ($LASTEXITCODE -eq 0 -and (Test-Path $backupFile) -and (Get-Item $backupFile).Length -gt 0) {
Write-Host "Volume backup completed successfully to $backupFile!" -ForegroundColor Green
# Optional: Compress the backup file
Write-Host "Compressing backup file..."
Compress-Archive -Path $backupFile -DestinationPath "$backupFile.zip" -Force
Remove-Item $backupFile
Write-Host "Backup compressed to $backupFile.zip" -ForegroundColor Green
} else {
Write-Host "Volume backup failed!" -ForegroundColor Red
}
# Optional: Clean up old volume backups (keep last 5)
$oldBackups = Get-ChildItem -Path $backupDir -Filter "postgres-volume-backup-*.zip" | Sort-Object LastWriteTime -Descending | Select-Object -Skip 5
foreach ($backup in $oldBackups) {
Remove-Item $backup.FullName
Write-Host "Removed old volume backup: $($backup.Name)"
}

67
restore-gitea-db.ps1 Normal file
View File

@@ -0,0 +1,67 @@
# Gitea Database Restore Script
param (
[Parameter(Mandatory=$true)]
[string]$BackupFile
)
# Check if backup file exists
if (-not (Test-Path $BackupFile)) {
Write-Host "Backup file not found: $BackupFile" -ForegroundColor Red
exit 1
}
# Extract the backup if it's a zip file
$tempFile = $null
if ($BackupFile.EndsWith(".zip")) {
$tempDir = [System.IO.Path]::GetTempPath()
$tempFile = Join-Path $tempDir "gitea-db-restore-temp.sql"
Write-Host "Extracting backup file..."
Expand-Archive -Path $BackupFile -DestinationPath $tempDir -Force
$extractedFile = Get-ChildItem -Path $tempDir -Filter "*.sql" | Select-Object -First 1
if ($extractedFile) {
Copy-Item $extractedFile.FullName -Destination $tempFile
$BackupFile = $tempFile
} else {
Write-Host "Failed to extract SQL file from backup" -ForegroundColor Red
exit 1
}
}
# Confirm before proceeding
Write-Host "WARNING: This will overwrite the current database with the backup." -ForegroundColor Yellow
Write-Host "Make sure your Gitea service is stopped before proceeding." -ForegroundColor Yellow
$confirmation = Read-Host "Do you want to continue? (y/n)"
if ($confirmation -ne "y") {
Write-Host "Restore cancelled."
if ($tempFile -and (Test-Path $tempFile)) {
Remove-Item $tempFile
}
exit 0
}
# Stop Gitea services
Write-Host "Stopping Gitea services..."
docker-compose stop
# Restore the database
Write-Host "Restoring database from backup..."
Get-Content $BackupFile | docker exec -i gitea-db psql -U gitea -d gitea
# Check restore status
if ($LASTEXITCODE -eq 0) {
Write-Host "Database restore completed successfully!" -ForegroundColor Green
} else {
Write-Host "Database restore failed!" -ForegroundColor Red
}
# Clean up temp file if created
if ($tempFile -and (Test-Path $tempFile)) {
Remove-Item $tempFile
}
# Restart Gitea services
Write-Host "Starting Gitea services..."
docker-compose start

88
restore-volume.ps1 Normal file
View File

@@ -0,0 +1,88 @@
# Script to restore the Postgres Docker volume from a backup
param (
[Parameter(Mandatory=$true)]
[string]$BackupFile
)
$volumeName = "gitea-docker_postgres-data"
$tempDir = Join-Path $env:TEMP "postgres-volume-restore"
# Check if backup file exists
if (-not (Test-Path $BackupFile)) {
Write-Host "Backup file not found: $BackupFile" -ForegroundColor Red
exit 1
}
# Extract the backup if it's a zip file
$tarFile = $BackupFile
if ($BackupFile.EndsWith(".zip")) {
# Create temp directory if it doesn't exist
if (-not (Test-Path $tempDir)) {
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
} else {
# Clean temp directory
Remove-Item -Path "$tempDir\*" -Force -Recurse -ErrorAction SilentlyContinue
}
Write-Host "Extracting backup file..."
Expand-Archive -Path $BackupFile -DestinationPath $tempDir -Force
$extractedFile = Get-ChildItem -Path $tempDir -Filter "*.tar" | Select-Object -First 1
if ($extractedFile) {
$tarFile = $extractedFile.FullName
} else {
Write-Host "Failed to extract TAR file from backup" -ForegroundColor Red
exit 1
}
}
# Confirm before proceeding
Write-Host "WARNING: This will overwrite the current database volume with the backup." -ForegroundColor Yellow
Write-Host "Make sure your Gitea services are stopped before proceeding." -ForegroundColor Yellow
$confirmation = Read-Host "Do you want to continue? (y/n)"
if ($confirmation -ne "y") {
Write-Host "Restore cancelled."
exit 0
}
# Stop Gitea services
Write-Host "Stopping Gitea services..."
docker-compose down
# Check if volume exists and remove it
$volumeExists = docker volume ls --format "{{.Name}}" | Select-String -Pattern "^$volumeName$"
if ($volumeExists) {
Write-Host "Removing existing volume $volumeName..."
docker volume rm $volumeName
if ($LASTEXITCODE -ne 0) {
Write-Host "Failed to remove existing volume. It might be in use by another container." -ForegroundColor Red
exit 1
}
}
# Create a new volume
Write-Host "Creating new volume $volumeName..."
docker volume create $volumeName
# Restore from backup
Write-Host "Restoring volume from backup..."
docker run --rm -v ${volumeName}:/volume -v ${tarFile}:/backup.tar alpine sh -c "cd /volume && tar -xf /backup.tar"
if ($LASTEXITCODE -eq 0) {
Write-Host "Volume restore completed successfully!" -ForegroundColor Green
} else {
Write-Host "Volume restore failed!" -ForegroundColor Red
exit 1
}
# Clean up temp directory if created
if ($BackupFile.EndsWith(".zip") -and (Test-Path $tempDir)) {
Remove-Item -Path $tempDir -Force -Recurse -ErrorAction SilentlyContinue
}
# Start Gitea services
Write-Host "Starting Gitea services..."
docker-compose up -d
Write-Host "Restore process completed. Check if your Gitea instance is working properly." -ForegroundColor Green

29
schedule-backup.ps1 Normal file
View File

@@ -0,0 +1,29 @@
# Script to create a scheduled task for Gitea database backups
$scriptPath = Join-Path (Get-Location) "backup-gitea-db.ps1"
$taskName = "GiteaDatabaseBackup"
$taskDescription = "Regular backup of Gitea PostgreSQL database"
# Check if the backup script exists
if (-not (Test-Path $scriptPath)) {
Write-Host "Backup script not found at: $scriptPath" -ForegroundColor Red
exit 1
}
# Create a scheduled task to run daily at 3 AM
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`""
$trigger = New-ScheduledTaskTrigger -Daily -At 3AM
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -DontStopOnIdleEnd -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
# Register the scheduled task
$taskExists = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
if ($taskExists) {
Write-Host "Task '$taskName' already exists. Updating..." -ForegroundColor Yellow
Set-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Settings $settings -Description $taskDescription
} else {
Write-Host "Creating new scheduled task '$taskName'..." -ForegroundColor Green
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Settings $settings -Description $taskDescription -User "$env:USERDOMAIN\$env:USERNAME"
}
Write-Host "Scheduled task setup complete. The database will be backed up daily at 3 AM." -ForegroundColor Green
Write-Host "Backup files will be stored in the 'backups' folder in your Gitea Docker directory." -ForegroundColor Green