# How to Set Up Auto-Backups with Configurable Retention in TREK

> Learn to set up auto-backups with configurable retention in TREK. This guide explains how to protect your data with automatic ZIP archives and retention pruning via the Admin panel or REST API.

- Repository: [Maurice/TREK](https://github.com/mauriceboe/TREK)
- Tags: how-to-guide
- Published: 2026-07-04

---

**TREK includes a built-in Auto-Backup feature that creates periodic ZIP archives of your SQLite database and uploads directory, with automatic retention pruning controlled via the Admin panel or REST API.**

The TREK travel management application (mauriceboe/TREK) ships with a complete server-side backup solution that requires no external cron jobs. The system handles scheduling, compression, and automatic cleanup based on a configurable retention window, all manageable from a single React-based interface or programmatically via REST endpoints.

## Where Auto-Backup Logic Lives in the Codebase

The auto-backup system spans multiple layers of the NestJS/TypeScript stack. Understanding these components helps when troubleshooting or extending the functionality.

- **Backup Controller** ([`server/src/nest/backup/backup.controller.ts`](https://github.com/mauriceboe/TREK/blob/main/server/src/nest/backup/backup.controller.ts)): Exposes HTTP endpoints under `/api/backup/*`. This file handles the `POST /api/backup/auto_settings` request that stores your schedule configuration and the `GET /api/backup/auto_settings` endpoint for retrieving current settings.

- **Backup Service & Schema** ([`shared/src/backup/backup.schema.ts`](https://github.com/mauriceboe/TREK/blob/main/shared/src/backup/backup.schema.ts)): Contains the core `createBackup()` routine that generates ZIP files containing `travel.db` and the `uploads/` directory. This service also implements the `deleteBackup()` method used during retention pruning.

- **Backup Scheduler**: A cron-like interval job (search for `BackupScheduler` in [`src/nest/backup/backup.scheduler.ts`](https://github.com/mauriceboe/TREK/blob/main/src/nest/backup/backup.scheduler.ts)) that runs once per minute to check if the current time matches your configured schedule and triggers the backup creation and subsequent pruning.

- **Client API Wrapper** ([`client/src/api/client.ts`](https://github.com/mauriceboe/TREK/blob/main/client/src/api/client.ts)): Provides frontend convenience functions like `backup.auto_settings()` that wrap the REST calls with TypeScript types.

- **Frontend UI**: React components in the `src/ui/backup/` directory render the Auto-Backup form, including fields for hour, day of week, day of month, and retention days.

- **Documentation** ([`wiki/Backups.md`](https://github.com/mauriceboe/TREK/blob/main/wiki/Backups.md)): The definitive reference for UI field descriptions and retention behavior.

## Understanding Retention and Core Concepts

Before configuring the schedule, understand how TREK handles file lifecycle and security.

**Retention Policy (`keepDays`)**: After each successful backup run, the scheduler deletes **all** backup files (both manual and auto-generated) older than the specified number of days. The default is `7` days. Setting this value to `0` disables pruning entirely, preserving backups indefinitely.

**File Naming and Storage**: Auto-backups are saved as `auto-backup-<timestamp>.zip` while manual snapshots use `backup-<timestamp>.zip`. Both reside in `data/backups/` relative to the server root.

**Upload Limits**: The server enforces a default 500 MiB size limit on backup uploads. Adjust this via the `BACKUP_UPLOAD_LIMIT_MB` environment variable if your `uploads/` directory exceeds this limit.

**Security Critical**: Backups **do not** contain the `ENCRYPTION_KEY`. You must back up this key separately; restoration will fail without it.

**Audit Logging**: Every action is logged with specific action types: `backup.auto_settings` when saving the schedule, `backup.create` when generating a file, and `backup.delete` during retention cleanup.

## Configuring Auto-Backups via the Web UI

The simplest way to enable scheduled backups is through the Admin panel.

1. Navigate to **Admin → Backups**.

2. In the **Auto-Backup** section, toggle **Enabled**.

3. Configure the schedule:

   - **Hour**: 0-23 (UTC time of day to trigger)
   - **Day of Week**: 0-6 for weekly schedules (Sunday=0)
   - **Day of Month**: 1-28 for monthly schedules (values 29-31 are ignored to prevent end-of-month edge cases)
   - **Retention – Keep last … days**: Number of days to retain backups. Use `0` for "keep forever".

4. Click **Save**. The client sends a `POST` request to `/api/backup/auto_settings` with your JSON payload.

## Configuring Auto-Backups via the REST API

For infrastructure-as-code or remote management, use the REST API directly.

| Method | Endpoint | Purpose |
|--------|----------|---------|
| **POST** | `/api/backup/auto_settings` | Create or update the schedule |
| **GET** | `/api/backup/auto_settings` | Retrieve current configuration |
| **DELETE** | `/api/backup/auto_settings` | Disable auto-backup (sets `enabled: false`) |

### Node.js/Axios Example

```typescript
import axios from 'axios';

const api = axios.create({ 
  baseURL: 'https://my-trek.example.com/api', 
  withCredentials: true 
});

async function enableAutoBackup() {
  await api.post('/backup/auto_settings', {
    enabled: true,
    hour: 2,          // Run at 02:00 UTC
    dayOfWeek: 0,    // Weekly on Sunday
    dayOfMonth: 1,   // Monthly on the 1st
    keepDays: 14     // Retain for 14 days
  });
  console.log('Auto-backup configured with 14-day retention');
}

enableAutoBackup().catch(console.error);

```

### cURL Example

```bash
curl -X POST -b cookies.txt \
  -H "Content-Type: application/json" \
  -d '{"enabled":true,"hour":3,"dayOfWeek":1,"dayOfMonth":1,"keepDays":30}' \
  https://my-trek.example.com/api/backup/auto_settings

```

## How Retention Pruning Works Under the Hood

The scheduler implemented in [`backup.scheduler.ts`](https://github.com/mauriceboe/TREK/blob/main/backup.scheduler.ts) executes the following logic after each backup creation:

```typescript
// Simplified flow from the actual implementation
async function runAutoBackup() {
  const settings = await Settings.getAutoBackup();
  if (!isTimeToRun(settings)) return;

  // Create the archive
  const zip = await backupService.createBackup();
  await auditLog({ action: 'backup.create', resource: zip.filename });

  // Retention pruning
  if (settings.keepDays > 0) {
    const cutoff = Date.now() - settings.keepDays * 24 * 60 * 60 * 1000;
    const oldBackups = await backupService.listBackups()
      .filter(b => b.timestamp < cutoff);
    
    for (const b of oldBackups) {
      await backupService.deleteBackup(b.filename);
      await auditLog({ action: 'backup.delete', resource: b.filename });
    }
  }
}

```

This runs every minute via the NestJS scheduler. When the current time matches your configured hour (and optional day/week constraints), it triggers the backup, then immediately prunes any files exceeding the retention window.

## Troubleshooting Common Issues

| Symptom | Cause | Solution |
|---------|-------|----------|
| **"413 Payload Too Large" on upload** | Reverse proxy `client_max_body_size` is below 500 MiB | Increase nginx/Caddy limit to match `BACKUP_UPLOAD_LIMIT_MB` |
| **Auto-backup never executes** | Timezone mismatch or scheduler not running | Verify server timezone and check logs for `BackupScheduler` startup messages |
| **Old backups remain after retention period** | `keepDays` set to `0` or negative | Set a positive integer (e.g., `30`) in the auto-settings |
| **Backup ZIP missing `uploads/` content** | Volume permissions issue | Ensure the container has write access to the `./uploads` directory |
| **Restore fails with decryption error** | Missing `ENCRYPTION_KEY` | Back up the encryption key separately; it is never stored inside the ZIP |

## Summary

- TREK's auto-backup is a server-side cron job that stores settings in the database and requires no external configuration.
- The system creates ZIP archives containing `travel.db` and the `uploads/` directory, storing them in `data/backups/`.
- Retention is controlled by the `keepDays` parameter, which triggers automatic deletion of files older than the specified threshold after each run.
- Configure via **Admin → Backups** or programmatically through `POST /api/backup/auto_settings`.
- All operations generate audit logs with actions `backup.create`, `backup.delete`, and `backup.auto_settings`.
- The `ENCRYPTION_KEY` must be backed up separately; it is excluded from all backup archives.

## Frequently Asked Questions

### What happens if I set keepDays to 0?

Setting `keepDays` to `0` disables the retention pruning entirely. The scheduler will create new backups according to your schedule but will never delete old files automatically. This is useful for compliance scenarios requiring long-term archival, but requires manual disk space monitoring.

### Why isn't my auto-backup running at the scheduled time?

The scheduler checks the system time against your configured hour once per minute. If the server timezone differs from your expectation, or if the `BackupScheduler` service failed to initialize (check server logs for startup errors), the job will not trigger. Ensure your Docker container or host system has the correct `TZ` environment variable set.

### Are manual backups affected by the retention policy?

Yes. The retention pruning logic in [`shared/src/backup/backup.schema.ts`](https://github.com/mauriceboe/TREK/blob/main/shared/src/backup/backup.schema.ts) treats all files in `data/backups/` equally, regardless of whether they were created manually via `POST /api/backup/create` or automatically. Any file older than the `keepDays` threshold is subject to deletion.

### How do I restore from an auto-backup?

Navigate to **Admin → Backups**, locate the `auto-backup-<timestamp>.zip` file in the list, and click **Restore**. Alternatively, use `POST /api/backup/upload_restore` with the ZIP file as multipart/form-data. Ensure you have the original `ENCRYPTION_KEY` available, as the database cannot be decrypted without it.