0
# Pruning and Maintenance
1
2
Archive pruning, repository maintenance, and cleanup operations for managing backup retention policies and optimizing repository storage in BorgBackup.
3
4
```python
5
import subprocess
6
```
7
8
## Capabilities
9
10
### Archive Pruning
11
12
Automatically remove old archives based on retention policies to manage storage space and backup history.
13
14
```python { .api }
15
def prune_archives(repo_path: str, keep_within: str = None, keep_daily: int = None,
16
keep_weekly: int = None, keep_monthly: int = None,
17
keep_yearly: int = None, dry_run: bool = False,
18
stats: bool = False, list_archives: bool = False) -> None:
19
"""
20
Prune archives based on retention policy.
21
22
Args:
23
repo_path: Path to repository
24
keep_within: Keep archives within time period (e.g., '1d', '7d', '1m', '1y')
25
keep_daily: Number of daily archives to keep
26
keep_weekly: Number of weekly archives to keep
27
keep_monthly: Number of monthly archives to keep
28
keep_yearly: Number of yearly archives to keep
29
dry_run: Show what would be pruned without pruning
30
stats: Show pruning statistics
31
list_archives: List archives that would be kept/pruned
32
"""
33
cmd = ['borg', 'prune']
34
if keep_within:
35
cmd.extend(['--keep-within', keep_within])
36
if keep_daily:
37
cmd.extend(['--keep-daily', str(keep_daily)])
38
if keep_weekly:
39
cmd.extend(['--keep-weekly', str(keep_weekly)])
40
if keep_monthly:
41
cmd.extend(['--keep-monthly', str(keep_monthly)])
42
if keep_yearly:
43
cmd.extend(['--keep-yearly', str(keep_yearly)])
44
if dry_run:
45
cmd.append('--dry-run')
46
if stats:
47
cmd.append('--stats')
48
if list_archives:
49
cmd.append('--list')
50
51
cmd.append(repo_path)
52
subprocess.run(cmd, check=True)
53
```
54
55
Usage example:
56
```python
57
import subprocess
58
59
# Prune with common retention policy
60
subprocess.run([
61
'borg', 'prune', '--stats',
62
'--keep-daily=7', # Keep 7 daily backups
63
'--keep-weekly=4', # Keep 4 weekly backups
64
'--keep-monthly=6', # Keep 6 monthly backups
65
'--keep-yearly=2', # Keep 2 yearly backups
66
'/backup/repo'
67
], check=True)
68
69
# Prune keeping archives within last 30 days
70
subprocess.run(['borg', 'prune', '--keep-within=30d', '/backup/repo'], check=True)
71
72
# Dry run to see what would be pruned
73
subprocess.run(['borg', 'prune', '--dry-run', '--list', '--keep-daily=7', '/backup/repo'], check=True)
74
```
75
76
### Repository Compaction
77
78
Reclaim space by removing deleted data and optimizing repository storage.
79
80
```python { .api }
81
def compact_repository(repo_path: str, threshold: float = None,
82
dry_run: bool = False, progress: bool = False) -> None:
83
"""
84
Compact repository to reclaim space.
85
86
Args:
87
repo_path: Path to repository
88
threshold: Minimum saved space ratio to trigger compaction (0.0-1.0)
89
dry_run: Show compaction statistics without compacting
90
progress: Show progress during compaction
91
"""
92
cmd = ['borg', 'compact']
93
if threshold:
94
cmd.extend(['--threshold', str(threshold)])
95
if dry_run:
96
cmd.append('--dry-run')
97
if progress:
98
cmd.append('--progress')
99
100
cmd.append(repo_path)
101
subprocess.run(cmd, check=True)
102
```
103
104
Usage example:
105
```python
106
import subprocess
107
108
# Compact repository with progress
109
subprocess.run(['borg', 'compact', '--progress', '/backup/repo'], check=True)
110
111
# Compact only if at least 20% space can be saved
112
subprocess.run(['borg', 'compact', '--threshold=0.2', '/backup/repo'], check=True)
113
114
# Dry run to see potential space savings
115
subprocess.run(['borg', 'compact', '--dry-run', '/backup/repo'], check=True)
116
```
117
118
### Cache Management
119
120
Manage local cache for improved performance, including cache deletion and recreation.
121
122
```python { .api }
123
def delete_cache(repo_path: str, cache_only: bool = False,
124
force: bool = False) -> None:
125
"""
126
Delete repository cache.
127
128
Args:
129
repo_path: Path to repository
130
cache_only: Delete only cache files, not security info
131
force: Force deletion without confirmation
132
"""
133
cmd = ['borg', 'delete']
134
if cache_only:
135
cmd.append('--cache-only')
136
if force:
137
cmd.append('--force')
138
139
cmd.append(repo_path)
140
subprocess.run(cmd, check=True)
141
142
def recreate_cache(repo_path: str, progress: bool = False) -> None:
143
"""
144
Recreate repository cache by reading all archives.
145
146
Args:
147
repo_path: Path to repository
148
progress: Show progress during cache recreation
149
"""
150
# Cache is recreated automatically on next access
151
# Force recreation by listing archives
152
cmd = ['borg', 'list']
153
if progress:
154
cmd.append('--progress')
155
cmd.append(repo_path)
156
subprocess.run(cmd, check=True)
157
```
158
159
Usage example:
160
```python
161
import subprocess
162
163
# Delete cache (will be recreated on next access)
164
subprocess.run(['borg', 'delete', '--cache-only', '/backup/repo'], check=True)
165
166
# Recreate cache by listing archives
167
subprocess.run(['borg', 'list', '/backup/repo'], check=True)
168
```
169
170
### Archive Recreation
171
172
Recreate archives with different settings, such as changing compression or excluding/including different files.
173
174
```python { .api }
175
def recreate_archive(repo_path: str, archive_name: str = None,
176
target_name: str = None, excludes: list = None,
177
compression: str = None, recompress: bool = False,
178
dry_run: bool = False, stats: bool = False) -> None:
179
"""
180
Recreate archive with modified settings.
181
182
Args:
183
repo_path: Path to repository
184
archive_name: Archive to recreate (or pattern)
185
target_name: New archive name
186
excludes: List of exclusion patterns
187
compression: New compression algorithm
188
recompress: Recompress all chunks
189
dry_run: Show what would be recreated
190
stats: Show recreation statistics
191
"""
192
cmd = ['borg', 'recreate']
193
if target_name:
194
cmd.extend(['--target', target_name])
195
if excludes:
196
for exclude in excludes:
197
cmd.extend(['--exclude', exclude])
198
if compression:
199
cmd.extend(['--compression', compression])
200
if recompress:
201
cmd.append('--recompress')
202
if dry_run:
203
cmd.append('--dry-run')
204
if stats:
205
cmd.append('--stats')
206
207
if archive_name:
208
cmd.append(f'{repo_path}::{archive_name}')
209
else:
210
cmd.append(repo_path)
211
212
subprocess.run(cmd, check=True)
213
```
214
215
Usage example:
216
```python
217
import subprocess
218
219
# Recreate archive with better compression
220
subprocess.run([
221
'borg', 'recreate', '--compression=zstd,9', '--recompress',
222
'/backup/repo::old-archive'
223
], check=True)
224
225
# Recreate excluding additional patterns
226
subprocess.run([
227
'borg', 'recreate', '--exclude=*.log', '--exclude=temp/*',
228
'/backup/repo::archive-name'
229
], check=True)
230
231
# Dry run to see what would change
232
subprocess.run([
233
'borg', 'recreate', '--dry-run', '--stats', '--compression=lzma,6',
234
'/backup/repo::test-archive'
235
], check=True)
236
```
237
238
### Repository Upgrading
239
240
Upgrade repository format to newer versions for improved features and performance.
241
242
```python { .api }
243
def upgrade_repository(repo_path: str, dry_run: bool = False,
244
force: bool = False, tam: bool = False) -> None:
245
"""
246
Upgrade repository format.
247
248
Args:
249
repo_path: Path to repository
250
dry_run: Show what would be upgraded
251
force: Force upgrade without confirmation
252
tam: Enable/disable TAM (Tamper Authentication Mode)
253
"""
254
cmd = ['borg', 'upgrade']
255
if dry_run:
256
cmd.append('--dry-run')
257
if force:
258
cmd.append('--force')
259
if tam:
260
cmd.append('--tam')
261
262
cmd.append(repo_path)
263
subprocess.run(cmd, check=True)
264
```
265
266
Usage example:
267
```python
268
import subprocess
269
270
# Upgrade repository (dry run first)
271
subprocess.run(['borg', 'upgrade', '--dry-run', '/backup/repo'], check=True)
272
273
# Perform actual upgrade
274
subprocess.run(['borg', 'upgrade', '/backup/repo'], check=True)
275
```
276
277
### Repository Serving
278
279
Serve repository over SSH for remote access with security restrictions.
280
281
```python { .api }
282
def serve_repository(restrict_to_paths: list = None, restrict_to_repositories: list = None,
283
append_only: bool = False, storage_quota: str = None) -> None:
284
"""
285
Serve repository for remote access (typically called via SSH).
286
287
Args:
288
restrict_to_paths: Restrict access to specific paths
289
restrict_to_repositories: Restrict access to specific repositories
290
append_only: Allow only append operations
291
storage_quota: Set storage quota limit
292
"""
293
cmd = ['borg', 'serve']
294
if restrict_to_paths:
295
cmd.extend(['--restrict-to-path'] + restrict_to_paths)
296
if restrict_to_repositories:
297
cmd.extend(['--restrict-to-repository'] + restrict_to_repositories)
298
if append_only:
299
cmd.append('--append-only')
300
if storage_quota:
301
cmd.extend(['--storage-quota', storage_quota])
302
303
subprocess.run(cmd, check=True)
304
```
305
306
Usage example:
307
```python
308
import subprocess
309
310
# Serve with path restrictions (typically in SSH authorized_keys)
311
# command="borg serve --restrict-to-path /backup/repos" ssh-rsa AAAA...
312
313
# Serve in append-only mode
314
# command="borg serve --append-only --restrict-to-path /backup/repo" ssh-rsa AAAA...
315
```
316
317
### Lock Management
318
319
Break repository and cache locks when operations are interrupted or stuck.
320
321
```python { .api }
322
def break_repository_lock(repo_path: str) -> None:
323
"""
324
Break repository and cache locks.
325
326
Args:
327
repo_path: Path to repository
328
329
Warning: Only use when no borg process is running
330
"""
331
cmd = ['borg', 'break-lock', repo_path]
332
subprocess.run(cmd, check=True)
333
```
334
335
Usage example:
336
```python
337
import subprocess
338
339
# Break locks when repository appears stuck
340
# WARNING: Ensure no other borg processes are running first!
341
subprocess.run(['borg', 'break-lock', '/backup/repo'], check=True)
342
```
343
344
## Types
345
346
```python { .api }
347
class PruneStats:
348
"""Pruning operation statistics"""
349
def __init__(self):
350
self.kept_archives: int # Number of archives kept
351
self.pruned_archives: int # Number of archives pruned
352
self.deleted_data: int # Bytes of data deleted
353
self.freed_space: int # Space freed in repository
354
355
class CompactionStats:
356
"""Repository compaction statistics"""
357
def __init__(self):
358
self.segments_before: int # Segments before compaction
359
self.segments_after: int # Segments after compaction
360
self.size_before: int # Size before compaction
361
self.size_after: int # Size after compaction
362
self.freed_space: int # Space freed by compaction
363
self.time_elapsed: float # Compaction time in seconds
364
365
class UpgradeInfo:
366
"""Repository upgrade information"""
367
def __init__(self):
368
self.version_before: str # Version before upgrade
369
self.version_after: str # Version after upgrade
370
self.tam_enabled: bool # TAM authentication enabled
371
self.changes_made: list # List of changes made
372
```