Add comprehensive backup system for Gitea database
This commit is contained in:
73
BACKUP-README.md
Normal file
73
BACKUP-README.md
Normal 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
|
||||
```
|
||||
56
README.md
56
README.md
@@ -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
32
backup-gitea-db.ps1
Normal 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
41
backup-volume.ps1
Normal 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
67
restore-gitea-db.ps1
Normal 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
88
restore-volume.ps1
Normal 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
29
schedule-backup.ps1
Normal 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
|
||||
Reference in New Issue
Block a user