/
/
/
1#!/bin/bash
2# ==============================================================================
3# Nginx Proxy Manager Backup Script
4# ==============================================================================
5#
6# Description: Creates comprehensive backups of Nginx Proxy Manager configuration
7# Usage: ./nginx-proxy-backup.sh [full|config-only|db-only|rotate]
8#
9# This script is automatically generated by Ansible - DO NOT EDIT MANUALLY
10# Template: nginx-proxy-backup.sh.j2
11#
12# ==============================================================================
13
14set -euo pipefail
15
16# Configuration
17NGINX_DATA_DIR="{{ docker_base_path }}/nginx-proxy/data"
18NGINX_LETSENCRYPT_DIR="{{ docker_base_path }}/nginx-proxy/letsencrypt"
19NGINX_MYSQL_DIR="{{ docker_base_path }}/nginx-proxy/mysql"
20BACKUP_DIR="{{ docker_base_path }}/backups/nginx-proxy"
21RETENTION_DAYS={{ backup_retention_days | default(7) }}
22LOG_FILE="/var/log/nginx-proxy-backup.log"
23TIMESTAMP=$(date +%Y%m%d-%H%M%S)
24
25# Database configuration
26DB_CONTAINER="{{ nginx_proxy_db_container_name }}"
27DB_NAME="{{ nginx_proxy_db_name }}"
28DB_USER="{{ nginx_proxy_db_user }}"
29DB_PASSWORD="{{ nginx_proxy_db_password }}"
30
31# Ensure directories exist
32mkdir -p "${BACKUP_DIR}"
33
34# Logging function
35log() {
36 echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "${LOG_FILE}"
37}
38
39# Error handling function
40error_exit() {
41 log "ERROR: $1"
42 exit 1
43}
44
45# Validate configuration directories
46validate_config() {
47 local dirs=("${NGINX_DATA_DIR}" "${NGINX_LETSENCRYPT_DIR}" "${NGINX_MYSQL_DIR}")
48
49 for dir in "${dirs[@]}"; do
50 if [[ ! -d "${dir}" ]]; then
51 log "WARNING: Configuration directory not found: ${dir}"
52 fi
53 done
54}
55
56# Create full backup (config + database)
57create_full_backup() {
58 local backup_file="${BACKUP_DIR}/nginx-proxy-full-${TIMESTAMP}.tar.gz"
59
60 log "Creating full Nginx Proxy Manager backup"
61 validate_config
62
63 # Create backup excluding large/unnecessary files
64 tar -czf "${backup_file}" \
65 -C "{{ docker_base_path }}" \
66 --exclude="*.log" \
67 --exclude="cache" \
68 --exclude="tmp" \
69 --exclude="*.tmp" \
70 nginx-proxy/data \
71 nginx-proxy/letsencrypt \
72 nginx-proxy/mysql \
73 2>/dev/null || true
74
75 if [[ -f "${backup_file}" ]]; then
76 log "Full backup created: ${backup_file}"
77 echo "Backup size: $(du -h "${backup_file}" | cut -f1)"
78 echo "Backup contents: $(tar -tzf "${backup_file}" | wc -l) files"
79 else
80 error_exit "Failed to create full backup"
81 fi
82}
83
84# Create configuration-only backup
85create_config_backup() {
86 local backup_file="${BACKUP_DIR}/nginx-proxy-config-${TIMESTAMP}.tar.gz"
87
88 log "Creating Nginx Proxy Manager configuration backup"
89
90 if [[ ! -d "${NGINX_DATA_DIR}" ]]; then
91 error_exit "Nginx data directory not found"
92 fi
93
94 # Backup config excluding database files
95 tar -czf "${backup_file}" \
96 -C "{{ docker_base_path }}" \
97 --exclude="mysql" \
98 --exclude="*.log" \
99 --exclude="cache" \
100 nginx-proxy/data \
101 nginx-proxy/letsencrypt \
102 2>/dev/null || true
103
104 if [[ -f "${backup_file}" ]]; then
105 log "Configuration backup created: ${backup_file}"
106 echo "Backup size: $(du -h "${backup_file}" | cut -f1)"
107 else
108 error_exit "Failed to create configuration backup"
109 fi
110}
111
112# Create database-only backup
113create_db_backup() {
114 local backup_file="${BACKUP_DIR}/nginx-proxy-db-${TIMESTAMP}.sql.gz"
115
116 log "Creating Nginx Proxy Manager database backup"
117
118 # Check if database container is running
119 if ! docker ps --format "table {{.Names}}" | grep -q "${DB_CONTAINER}"; then
120 error_exit "Database container is not running"
121 fi
122
123 # Create database dump
124 if docker exec "${DB_CONTAINER}" mysqldump \
125 -u "${DB_USER}" \
126 -p"${DB_PASSWORD}" \
127 "${DB_NAME}" \
128 --single-transaction \
129 --routines \
130 --triggers \
131 --events 2>/dev/null | gzip > "${backup_file}"; then
132
133 log "Database backup created: ${backup_file}"
134 echo "Backup size: $(du -h "${backup_file}" | cut -f1)"
135 else
136 error_exit "Failed to create database backup"
137 fi
138}
139
140# Rotate old backups
141rotate_backups() {
142 log "Rotating backups older than ${RETENTION_DAYS} days"
143
144 local files_removed=0
145 local space_freed=0
146
147 # Find and remove old backup files
148 find "${BACKUP_DIR}" -name "nginx-proxy-*" -mtime +${RETENTION_DAYS} -print0 | while IFS= read -r -d '' file; do
149 local file_size=$(du -b "${file}" | cut -f1)
150 rm -f "${file}"
151 files_removed=$((files_removed + 1))
152 space_freed=$((space_freed + file_size))
153 log "Removed old backup: ${file}"
154 done
155
156 if [[ ${files_removed} -gt 0 ]]; then
157 log "Rotation complete: ${files_removed} files removed, $(numfmt --to=iec ${space_freed}) freed"
158 else
159 log "No old backups found for rotation"
160 fi
161}
162
163# Verify backup integrity
164verify_backup() {
165 local backup_file="${1}"
166
167 if [[ ! -f "${backup_file}" ]]; then
168 error_exit "Backup file not found: ${backup_file}"
169 fi
170
171 log "Verifying backup integrity: ${backup_file}"
172
173 # Check based on file type
174 if [[ "${backup_file}" == *.tar.gz ]]; then
175 # Test tar archive
176 if tar -tzf "${backup_file}" >/dev/null 2>&1; then
177 local file_count=$(tar -tzf "${backup_file}" | wc -l)
178 log "Backup verification successful - ${file_count} files"
179 else
180 error_exit "Backup verification failed - archive may be corrupt"
181 fi
182 elif [[ "${backup_file}" == *.sql.gz ]]; then
183 # Test gzipped SQL file
184 if gzip -t "${backup_file}" 2>/dev/null; then
185 log "Database backup verification successful"
186 else
187 error_exit "Database backup verification failed - file may be corrupt"
188 fi
189 else
190 log "WARNING: Unknown backup file type - skipping verification"
191 fi
192}
193
194# List available backups
195list_backups() {
196 log "Available Nginx Proxy Manager backups:"
197
198 if ls "${BACKUP_DIR}"/nginx-proxy-* 1>/dev/null 2>&1; then
199 echo "Backup files in ${BACKUP_DIR}:"
200 ls -la "${BACKUP_DIR}"/nginx-proxy-*
201 echo ""
202 echo "Total backup size: $(du -sh "${BACKUP_DIR}" | cut -f1)"
203 echo "Number of backups: $(ls "${BACKUP_DIR}"/nginx-proxy-* 2>/dev/null | wc -l)"
204 else
205 echo "No backup files found in ${BACKUP_DIR}"
206 fi
207}
208
209# Export Let's Encrypt certificates separately
210export_certificates() {
211 local certs_dir="${BACKUP_DIR}/letsencrypt-${TIMESTAMP}"
212
213 log "Exporting Let's Encrypt certificates"
214
215 if [[ -d "${NGINX_LETSENCRYPT_DIR}" ]]; then
216 mkdir -p "${certs_dir}"
217 cp -r "${NGINX_LETSENCRYPT_DIR}"/* "${certs_dir}"/ 2>/dev/null || true
218
219 # Create archive
220 tar -czf "${certs_dir}.tar.gz" -C "${BACKUP_DIR}" "letsencrypt-${TIMESTAMP}" 2>/dev/null || true
221 rm -rf "${certs_dir}"
222
223 log "Certificates exported: ${certs_dir}.tar.gz"
224 else
225 log "WARNING: Let's Encrypt directory not found"
226 fi
227}
228
229# Show usage
230usage() {
231 cat << EOF
232Nginx Proxy Manager Backup Script
233
234Usage: $0 [command]
235
236Commands:
237 full Create full backup (config + database)
238 config-only Create configuration-only backup
239 db-only Create database-only backup
240 rotate Remove backups older than ${RETENTION_DAYS} days
241 list List available backups
242 verify [file] Verify backup file integrity
243 certs Export Let's Encrypt certificates separately
244 help Show this help message
245
246Examples:
247 $0 full
248 $0 config-only
249 $0 db-only
250 $0 rotate
251 $0 list
252 $0 verify /backups/nginx-proxy/nginx-proxy-full-20231201-120000.tar.gz
253 $0 certs
254
255Backup retention: ${RETENTION_DAYS} days
256Backup directory: ${BACKUP_DIR}
257EOF
258}
259
260# Main execution
261main() {
262 local command="${1:-help}"
263
264 case "${command}" in
265 full)
266 create_full_backup
267 ;;
268 config-only)
269 create_config_backup
270 ;;
271 db-only)
272 create_db_backup
273 ;;
274 rotate)
275 rotate_backups
276 ;;
277 list)
278 list_backups
279 ;;
280 verify)
281 verify_backup "${2:-}"
282 ;;
283 certs)
284 export_certificates
285 ;;
286 help|*)
287 usage
288 ;;
289 esac
290}
291
292# Run main function with all arguments
293main "$@"