moved documentation into separate example sub directory
This commit is contained in:
parent
8c3e85cb50
commit
7b2bb251fd
8 changed files with 442 additions and 420 deletions
397
README.md
397
README.md
|
|
@ -37,396 +37,6 @@ Zerobyte is a backup automation tool that helps you save your data across multip
|
|||
|
||||
In order to run Zerobyte, you need to have Docker and Docker Compose installed on your server. Then, you can use the provided `docker-compose.yml` file to start the application.
|
||||
|
||||
### Configure Zerobyte via Config File
|
||||
|
||||
|
||||
You can pre-configure backup sources (volumes), destinations (repositories), backup schedules, notification destinations and initial users using a config file (`zerobyte.config.json` by default (mounted in /app dir), or set `ZEROBYTE_CONFIG_PATH`).
|
||||
|
||||
Config import is opt-in. Enable it by setting `ZEROBYTE_CONFIG_IMPORT=true`.
|
||||
|
||||
Secrets/credentials in the config file can reference environment variables using `${VAR_NAME}` syntax for secure injection.
|
||||
|
||||
> **ℹ️ Config File Behavior**
|
||||
>
|
||||
> The config file is applied on startup using a **create-only** approach:
|
||||
> - Resources defined in the config are only created if they don't already exist in the database
|
||||
> - Existing resources with the same name are **not overwritten** - a warning is logged and the config entry is skipped
|
||||
> - Changes made via the UI are preserved across container restarts
|
||||
> - To update a resource from config, either modify it via the UI or delete it first
|
||||
>
|
||||
> This means the config file serves as "initial setup" rather than "desired state sync".
|
||||
|
||||
#### zerobyte.config.json Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"recoveryKey": "${RECOVERY_KEY}",
|
||||
"volumes": [
|
||||
// Array of volume objects. Each must have a unique "name" and a "config" matching one of the types below.
|
||||
],
|
||||
"repositories": [
|
||||
// Array of repository objects. Each must have a unique "name" and a "config" matching one of the types below.
|
||||
// Optionally, "compressionMode" ("auto", "off", "max")
|
||||
],
|
||||
"backupSchedules": [
|
||||
// Array of backup schedule objects as described below.
|
||||
],
|
||||
"notificationDestinations": [
|
||||
// Array of notification destination objects as described below.
|
||||
],
|
||||
"users": [
|
||||
// Array of user objects. Each must have a unique "username".
|
||||
// Note: Zerobyte currently supports a single user; only the first entry is applied.
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
##### Volume Types
|
||||
|
||||
- **Local Directory**
|
||||
```json
|
||||
{
|
||||
"name": "local-volume",
|
||||
"config": {
|
||||
"backend": "directory",
|
||||
"path": "/data",
|
||||
"readOnly": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **NFS**
|
||||
```json
|
||||
{
|
||||
"name": "nfs-volume",
|
||||
"config": {
|
||||
"backend": "nfs",
|
||||
"server": "nfs.example.com",
|
||||
"exportPath": "/data",
|
||||
"port": 2049,
|
||||
"version": "4",
|
||||
"readOnly": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **SMB**
|
||||
```json
|
||||
{
|
||||
"name": "smb-volume",
|
||||
"config": {
|
||||
"backend": "smb",
|
||||
"server": "smb.example.com",
|
||||
"share": "shared",
|
||||
"username": "user",
|
||||
"password": "${SMB_PASSWORD}",
|
||||
"vers": "3.0",
|
||||
"domain": "WORKGROUP",
|
||||
"port": 445,
|
||||
"readOnly": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **WebDAV**
|
||||
```json
|
||||
{
|
||||
"name": "webdav-volume",
|
||||
"config": {
|
||||
"backend": "webdav",
|
||||
"server": "webdav.example.com",
|
||||
"path": "/remote.php/webdav",
|
||||
"username": "user",
|
||||
"password": "${WEBDAV_PASSWORD}",
|
||||
"port": 80,
|
||||
"readOnly": false,
|
||||
"ssl": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### Repository Types
|
||||
|
||||
- **Local Directory**
|
||||
```json
|
||||
{
|
||||
"name": "local-repo",
|
||||
"config": {
|
||||
"backend": "local",
|
||||
"path": "/var/lib/zerobyte/repositories"
|
||||
},
|
||||
"compressionMode": "auto"
|
||||
}
|
||||
```
|
||||
> **Note for importing existing local repositories:** If you're migrating an existing repository (e.g., from a backup or another Zerobyte instance), include the `name` field in `config` with the original subfolder name, and set `isExistingRepository: true`. The actual restic repo is stored at `{path}/{name}`.
|
||||
>
|
||||
> **Example (migration):**
|
||||
> ```json
|
||||
> {
|
||||
> "name": "my-local-repo",
|
||||
> "config": {
|
||||
> "backend": "local",
|
||||
> "path": "/var/lib/zerobyte/repositories",
|
||||
> "name": "abc123",
|
||||
> "isExistingRepository": true
|
||||
> }
|
||||
> }
|
||||
> ```
|
||||
> You can find the `config.name` value in an exported config under `repositories[].config.name`. This value must be unique across all repositories.
|
||||
|
||||
- **S3-Compatible**
|
||||
```json
|
||||
{
|
||||
"name": "backup-repo",
|
||||
"config": {
|
||||
"backend": "s3",
|
||||
"bucket": "mybucket",
|
||||
"accessKeyId": "${ACCESS_KEY_ID}",
|
||||
"secretAccessKey": "${SECRET_ACCESS_KEY}"
|
||||
},
|
||||
"compressionMode": "auto"
|
||||
}
|
||||
```
|
||||
|
||||
- **Google Cloud Storage**
|
||||
```json
|
||||
{
|
||||
"name": "gcs-repo",
|
||||
"config": {
|
||||
"backend": "gcs",
|
||||
"bucket": "mybucket",
|
||||
"projectId": "my-gcp-project",
|
||||
"credentialsJson": "${GCS_CREDENTIALS}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Azure Blob Storage**
|
||||
```json
|
||||
{
|
||||
"name": "azure-repo",
|
||||
"config": {
|
||||
"backend": "azure",
|
||||
"container": "mycontainer",
|
||||
"accountName": "myaccount",
|
||||
"accountKey": "${AZURE_KEY}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **WebDAV, rclone, SFTP, REST, etc.**
|
||||
(See documentation for required fields; all support env variable secrets.)
|
||||
|
||||
##### Backup Schedules
|
||||
|
||||
- **Example:**
|
||||
```json
|
||||
{
|
||||
"name": "local-volume-local-repo",
|
||||
"volume": "local-volume",
|
||||
"repository": "local-repo",
|
||||
"cronExpression": "0 2 * * *",
|
||||
"retentionPolicy": { "keepLast": 7, "keepDaily": 7 },
|
||||
"includePatterns": ["important-folder"],
|
||||
"excludePatterns": ["*.tmp", "*.log"],
|
||||
"enabled": true,
|
||||
"notifications": ["slack-alerts", "email-admin"]
|
||||
}
|
||||
```
|
||||
- **Fields:**
|
||||
- `name`: Unique name of the schedule
|
||||
- `volume`: Name of the source volume
|
||||
- `repository`: Name of the destination repository
|
||||
- `cronExpression`: Cron string for schedule
|
||||
- `retentionPolicy`: Object with retention rules (e.g., keepLast, keepDaily)
|
||||
- `includePatterns`/`excludePatterns`: Arrays of patterns
|
||||
- `enabled`: Boolean
|
||||
- `notifications`: Array of notification destination names (strings) or detailed objects:
|
||||
- Simple: `["slack-alerts", "email-admin"]`
|
||||
- Detailed: `[{"name": "slack-alerts", "notifyOnStart": false, "notifyOnSuccess": true, "notifyOnWarning": true, "notifyOnFailure": true}]`
|
||||
|
||||
##### Notification Destinations
|
||||
|
||||
- **Examples:**
|
||||
- **Slack**
|
||||
```json
|
||||
{
|
||||
"name": "slack-alerts",
|
||||
"config": {
|
||||
"type": "slack",
|
||||
"webhookUrl": "${SLACK_WEBHOOK_URL}",
|
||||
"channel": "#backups",
|
||||
"username": "zerobyte",
|
||||
"iconEmoji": ":floppy_disk:"
|
||||
}
|
||||
}
|
||||
```
|
||||
- **Email**
|
||||
```json
|
||||
{
|
||||
"name": "email-admin",
|
||||
"config": {
|
||||
"type": "email",
|
||||
"smtpHost": "smtp.example.com",
|
||||
"smtpPort": 587,
|
||||
"username": "admin@example.com",
|
||||
"password": "${EMAIL_PASSWORD}",
|
||||
"from": "zerobyte@example.com",
|
||||
"to": ["admin@example.com"],
|
||||
"useTLS": true
|
||||
}
|
||||
}
|
||||
```
|
||||
- **Discord**
|
||||
```json
|
||||
{
|
||||
"name": "discord-backups",
|
||||
"config": {
|
||||
"type": "discord",
|
||||
"webhookUrl": "${DISCORD_WEBHOOK_URL}",
|
||||
"username": "zerobyte",
|
||||
"avatarUrl": "https://example.com/avatar.png",
|
||||
"threadId": "1234567890"
|
||||
}
|
||||
}
|
||||
```
|
||||
- **Gotify**
|
||||
```json
|
||||
{
|
||||
"name": "gotify-notify",
|
||||
"config": {
|
||||
"type": "gotify",
|
||||
"serverUrl": "https://gotify.example.com",
|
||||
"token": "${GOTIFY_TOKEN}",
|
||||
"path": "/message",
|
||||
"priority": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
- **ntfy**
|
||||
```json
|
||||
{
|
||||
"name": "ntfy-notify",
|
||||
"config": {
|
||||
"type": "ntfy",
|
||||
"serverUrl": "https://ntfy.example.com",
|
||||
"topic": "zerobyte-backups",
|
||||
"priority": "high",
|
||||
"username": "ntfyuser",
|
||||
"password": "${NTFY_PASSWORD}"
|
||||
}
|
||||
}
|
||||
```
|
||||
- **Pushover**
|
||||
```json
|
||||
{
|
||||
"name": "pushover-notify",
|
||||
"config": {
|
||||
"type": "pushover",
|
||||
"userKey": "${PUSHOVER_USER_KEY}",
|
||||
"apiToken": "${PUSHOVER_API_TOKEN}",
|
||||
"devices": "phone,tablet",
|
||||
"priority": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
- **Telegram**
|
||||
```json
|
||||
{
|
||||
"name": "telegram-notify",
|
||||
"config": {
|
||||
"type": "telegram",
|
||||
"botToken": "${TELEGRAM_BOT_TOKEN}",
|
||||
"chatId": "123456789"
|
||||
}
|
||||
}
|
||||
```
|
||||
- **Custom (shoutrrr)**
|
||||
```json
|
||||
{
|
||||
"name": "custom-shoutrrr",
|
||||
"config": {
|
||||
"type": "custom",
|
||||
"shoutrrrUrl": "${SHOUTRRR_URL}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Fields:**
|
||||
- `name`: Unique name for the notification config
|
||||
- `config.type`: Notification type (email, slack, discord, gotify, ntfy, pushover, telegram, custom)
|
||||
- `config`: Type-specific config with `type` field, secrets via `${ENV_VAR}`
|
||||
|
||||
##### User Setup (Automated)
|
||||
|
||||
Zerobyte currently supports a **single user**. If multiple entries are provided in `users[]`, only the first one will be applied.
|
||||
|
||||
- **Example (new instance):**
|
||||
```json
|
||||
{
|
||||
"recoveryKey": "${RECOVERY_KEY}",
|
||||
"users": [
|
||||
{
|
||||
"username": "my-user",
|
||||
"password": "${ADMIN_PASSWORD}"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- **Example (migration from another instance):**
|
||||
```json
|
||||
{
|
||||
"recoveryKey": "${RECOVERY_KEY}",
|
||||
"users": [
|
||||
{
|
||||
"username": "my-user",
|
||||
"passwordHash": "$argon2id$v=19$m=19456,t=2,p=1$..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- **Fields:**
|
||||
- `recoveryKey`: Optional recovery key (can use `${ENV_VAR}`) - if provided, the UI prompt to download recovery key will be skipped
|
||||
- `users[]`: List of users to create on first startup (create-only). Only the first user is applied.
|
||||
- `users[].username`: Username
|
||||
- `users[].password`: Plaintext password for new instances (can use `${ENV_VAR}`)
|
||||
- `users[].passwordHash`: Pre-hashed password for migration (exported from another instance)
|
||||
- `users[].hasDownloadedResticPassword`: Optional boolean; defaults to `true` when `recoveryKey` is provided
|
||||
|
||||
> **Note:** Use either `password` OR `passwordHash`, not both. The `passwordHash` option is useful when migrating from another Zerobyte instance using an exported config with `includePasswordHash=true`.
|
||||
|
||||
**On first startup, Zerobyte will automatically create users from the config file.**
|
||||
|
||||
> **⚠️ About the Recovery Key**
|
||||
>
|
||||
> The recovery key is a 64-character hex string that serves two critical purposes:
|
||||
> 1. **Restic repository password** - Used to encrypt all your backup data
|
||||
> 2. **Database encryption key** - Used to encrypt credentials stored in Zerobyte's database
|
||||
>
|
||||
> **If you lose this key, you will lose access to all your backups and stored credentials.**
|
||||
>
|
||||
> **Generating a recovery key ahead of time:**
|
||||
> ```bash
|
||||
> # Using OpenSSL (Linux/macOS)
|
||||
> openssl rand -hex 32
|
||||
>
|
||||
> # Using Python
|
||||
> python3 -c "import secrets; print(secrets.token_hex(32))"
|
||||
> ```
|
||||
>
|
||||
> **Retrieving from an existing instance:**
|
||||
> - Download via UI: Settings → Download Recovery Key
|
||||
> - Or read directly from the container: `docker exec zerobyte cat /var/lib/zerobyte/data/restic.pass`
|
||||
|
||||
---
|
||||
|
||||
**Notes:**
|
||||
- All secrets (passwords, keys) can use `${ENV_VAR}` syntax to inject from environment variables.
|
||||
- All paths must be accessible inside the container (mount host paths as needed).
|
||||
- `readOnly` is supported for all volume types that allow it, including local directories.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
zerobyte:
|
||||
|
|
@ -444,7 +54,6 @@ services:
|
|||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /var/lib/zerobyte:/var/lib/zerobyte
|
||||
- ./zerobyte.config.json:/app/zerobyte.config.json:ro # Mount your config file
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
|
|
@ -493,6 +102,12 @@ If you need remote mount capabilities, keep the original configuration with `cap
|
|||
|
||||
See [examples/README.md](examples/README.md) for runnable, copy/paste-friendly examples.
|
||||
|
||||
### Config file import (Infrastructure as Code)
|
||||
|
||||
If you want Zerobyte to create volumes, repositories, schedules, notification destinations, and an initial user from a JSON file on startup, check the following example:
|
||||
|
||||
- [examples/config-file-import/README.md](examples/config-file-import/README.md)
|
||||
|
||||
## Adding your first volume
|
||||
|
||||
Zerobyte supports multiple volume backends including NFS, SMB, WebDAV, and local directories. A volume represents the source data you want to back up and monitor.
|
||||
|
|
|
|||
|
|
@ -12,14 +12,11 @@ services:
|
|||
- SYS_ADMIN
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- ZEROBYTE_CONFIG_IMPORT=true
|
||||
ports:
|
||||
- "4096:4096"
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /var/lib/zerobyte:/var/lib/zerobyte
|
||||
- ./mydata:/mydata:ro
|
||||
- ./zerobyte.config.json:/app/zerobyte.config.json:ro
|
||||
- ./app:/app/app
|
||||
- ~/.config/rclone:/root/.config/rclone
|
||||
|
||||
|
|
@ -36,33 +33,7 @@ services:
|
|||
- SYS_ADMIN
|
||||
ports:
|
||||
- "4096:4096"
|
||||
environment:
|
||||
# Cloud storage credentials
|
||||
- ACCESS_KEY_ID=your-access-key-id
|
||||
- SECRET_ACCESS_KEY=your-secret-access-key
|
||||
- GCS_CREDENTIALS=your-gcs-credentials-json
|
||||
- AZURE_KEY=your-azure-key
|
||||
# Volume credentials
|
||||
- SMB_PASSWORD=your-smb-password
|
||||
- WEBDAV_PASSWORD=your-webdav-password
|
||||
# SFTP credentials (for repositories)
|
||||
- SFTP_PRIVATE_KEY=your-sftp-private-key
|
||||
# Notification credentials
|
||||
- SLACK_WEBHOOK_URL=your-slack-webhook-url
|
||||
- EMAIL_PASSWORD=your-email-password
|
||||
- DISCORD_WEBHOOK_URL=your-discord-webhook-url
|
||||
- GOTIFY_TOKEN=your-gotify-token
|
||||
- NTFY_PASSWORD=your-ntfy-password
|
||||
- PUSHOVER_USER_KEY=your-pushover-user-key
|
||||
- PUSHOVER_API_TOKEN=your-pushover-api-token
|
||||
- TELEGRAM_BOT_TOKEN=your-telegram-bot-token
|
||||
- SHOUTRRR_URL=your-shoutrrr-url
|
||||
# Admin credentials
|
||||
- ADMIN_PASSWORD=your-admin-password
|
||||
- RECOVERY_KEY=your-64-char-hex-recovery-key
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /var/lib/zerobyte:/var/lib/zerobyte
|
||||
- ./zerobyte.config.json:/app/zerobyte.config.json:ro
|
||||
- ./mydata:/mydata:ro
|
||||
- ~/.config/rclone:/root/.config/rclone
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ This folder contains runnable, copy/paste-friendly examples for running Zerobyte
|
|||
- [Bind-mount a local directory](directory-bind-mount/README.md) — back up a host folder by mounting it into the container.
|
||||
- [Mount an rclone config](rclone-config-mount/README.md) — use rclone-based repository backends by mounting your rclone config.
|
||||
- [Secret placeholders + Docker secrets](secrets-placeholders/README.md) — keep secrets out of the DB using `env://...` and `file://...` references.
|
||||
- [Config file import (Infrastructure as Code)](config-file-import/README.md) — pre-configure volumes/repos/schedules/users on startup.
|
||||
|
||||
### Advanced setups
|
||||
|
||||
|
|
|
|||
23
examples/config-file-import/.env.example
Normal file
23
examples/config-file-import/.env.example
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Copy to .env and fill values
|
||||
|
||||
# Used by examples/config-file-import/zerobyte.config.example.json
|
||||
RECOVERY_KEY=your-64-char-hex-recovery-key
|
||||
ADMIN_PASSWORD=change-me
|
||||
|
||||
# Optional: referenced by some config examples
|
||||
ACCESS_KEY_ID=
|
||||
SECRET_ACCESS_KEY=
|
||||
GCS_CREDENTIALS=
|
||||
AZURE_KEY=
|
||||
SMB_PASSWORD=
|
||||
WEBDAV_PASSWORD=
|
||||
SFTP_PRIVATE_KEY=
|
||||
SLACK_WEBHOOK_URL=
|
||||
EMAIL_PASSWORD=
|
||||
DISCORD_WEBHOOK_URL=
|
||||
GOTIFY_TOKEN=
|
||||
NTFY_PASSWORD=
|
||||
PUSHOVER_USER_KEY=
|
||||
PUSHOVER_API_TOKEN=
|
||||
TELEGRAM_BOT_TOKEN=
|
||||
SHOUTRRR_URL=
|
||||
2
examples/config-file-import/.gitignore
vendored
Normal file
2
examples/config-file-import/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
.env
|
||||
zerobyte.config.json
|
||||
388
examples/config-file-import/README.md
Normal file
388
examples/config-file-import/README.md
Normal file
|
|
@ -0,0 +1,388 @@
|
|||
# Config file import (Infrastructure as Code)
|
||||
|
||||
Zerobyte supports **config file import** on startup.
|
||||
This lets you pre-configure volumes, repositories, backup schedules, notification destinations, and an initial user.
|
||||
|
||||
This example includes:
|
||||
|
||||
- a runnable `docker-compose.yml`
|
||||
- a comprehensive `zerobyte.config.example.json` template (trim it down to what you actually use)
|
||||
- `.env.example` showing how to inject secrets via environment variables
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Docker + Docker Compose
|
||||
|
||||
This example includes `SYS_ADMIN` and `/dev/fuse` because it’s compatible with remote volume mounts (SMB/NFS/WebDAV).
|
||||
|
||||
## Setup
|
||||
|
||||
1. Copy the env file:
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
2. Create a local directory to mount as a sample volume:
|
||||
|
||||
```bash
|
||||
mkdir -p mydata
|
||||
```
|
||||
|
||||
3. Create a working config file (copy the example template):
|
||||
|
||||
```bash
|
||||
cp zerobyte.config.example.json zerobyte.config.json
|
||||
```
|
||||
|
||||
This is the recommended workflow for quick testing: if you don't have your own JSON config yet, start from the template.
|
||||
|
||||
4. Review/edit `zerobyte.config.json`.
|
||||
|
||||
The example template is intentionally "kitchen-sink" (lots of volume/repository/notification types) so you can copy what you need.
|
||||
Delete the entries you don't plan to use, and keep only the ones you have credentials/mounts for.
|
||||
|
||||
5. Start Zerobyte:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
6. Access the UI at `http://localhost:4096`.
|
||||
|
||||
## Notes
|
||||
|
||||
### Enabling import
|
||||
|
||||
Config import is opt-in and only runs when:
|
||||
|
||||
- `ZEROBYTE_CONFIG_IMPORT=true`
|
||||
|
||||
The config path defaults to `/app/zerobyte.config.json`, but you can override it via:
|
||||
|
||||
- `ZEROBYTE_CONFIG_PATH=/app/your-config.json`
|
||||
|
||||
### Secrets via env vars
|
||||
|
||||
Zerobyte supports **two different mechanisms** that are easy to confuse:
|
||||
|
||||
1. **Config import interpolation** (this example)
|
||||
2. **Secret placeholders** (`env://...` and `file://...`)
|
||||
|
||||
#### 1) Config import interpolation: `${VAR_NAME}`
|
||||
|
||||
During config import, any string value in the JSON can reference an environment variable using `${VAR_NAME}`.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"recoveryKey": "${RECOVERY_KEY}",
|
||||
"repositories": [
|
||||
{
|
||||
"name": "s3-repo",
|
||||
"config": {
|
||||
"backend": "s3",
|
||||
"accessKeyId": "${ACCESS_KEY_ID}",
|
||||
"secretAccessKey": "${SECRET_ACCESS_KEY}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Important properties of `${...}` interpolation:
|
||||
|
||||
- It runs **only during import**.
|
||||
- Values are **resolved before** they are written to the database (meaning the actual secret ends up in the DB for fields that are stored as secrets).
|
||||
- Because it reads `process.env`, Docker Compose must inject those variables into the container.
|
||||
|
||||
This example uses:
|
||||
|
||||
- `env_file: .env`
|
||||
|
||||
So to make `${VAR_NAME}` work, put the variables in `.env` (or otherwise provide them in the container environment).
|
||||
|
||||
#### 2) Secret placeholders: `env://...` and `file://...`
|
||||
|
||||
Separately from config import, Zerobyte supports **secret placeholders** for *some sensitive fields*.
|
||||
These placeholders are stored **as-is** in the database (the raw secret is not stored) and resolved at runtime.
|
||||
|
||||
Supported formats:
|
||||
|
||||
- `env://VAR_NAME` → reads `process.env.VAR_NAME` at runtime
|
||||
- `file://secret_name` → reads `/run/secrets/secret_name` (Docker secrets)
|
||||
|
||||
This is useful when you want to keep secrets out of the database and rotate them without editing Zerobyte’s stored config.
|
||||
|
||||
See the runnable example:
|
||||
|
||||
- [examples/secrets-placeholders/README.md](../secrets-placeholders/README.md)
|
||||
|
||||
### Config file behavior (create-only)
|
||||
|
||||
The config file is applied on startup using a **create-only** approach:
|
||||
|
||||
- Resources defined in the config are only created if they don't already exist in the database
|
||||
- Existing resources with the same name are **not overwritten** (a warning is logged and the config entry is skipped)
|
||||
- Changes made via the UI are preserved across container restarts
|
||||
- To update a resource from config, either modify it via the UI or delete it first
|
||||
|
||||
This makes the config file better suited as "initial setup" than as a "desired state sync".
|
||||
|
||||
---
|
||||
|
||||
## Config structure reference
|
||||
|
||||
This example is intended to be the primary, copy/paste-friendly reference for config import.
|
||||
|
||||
### `zerobyte.config.json` structure
|
||||
|
||||
```json
|
||||
{
|
||||
"recoveryKey": "${RECOVERY_KEY}",
|
||||
"volumes": [
|
||||
"..."
|
||||
],
|
||||
"repositories": [
|
||||
"..."
|
||||
],
|
||||
"backupSchedules": [
|
||||
"..."
|
||||
],
|
||||
"notificationDestinations": [
|
||||
"..."
|
||||
],
|
||||
"users": [
|
||||
"..."
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Volume types
|
||||
|
||||
#### Local directory
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "local-volume",
|
||||
"config": {
|
||||
"backend": "directory",
|
||||
"path": "/mydata",
|
||||
"readOnly": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### NFS
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "nfs-volume",
|
||||
"config": {
|
||||
"backend": "nfs",
|
||||
"server": "nfs.example.com",
|
||||
"exportPath": "/data",
|
||||
"port": 2049,
|
||||
"version": "4",
|
||||
"readOnly": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### SMB
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "smb-volume",
|
||||
"config": {
|
||||
"backend": "smb",
|
||||
"server": "smb.example.com",
|
||||
"share": "shared",
|
||||
"username": "user",
|
||||
"password": "${SMB_PASSWORD}",
|
||||
"vers": "3.0",
|
||||
"domain": "WORKGROUP",
|
||||
"port": 445,
|
||||
"readOnly": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### WebDAV
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "webdav-volume",
|
||||
"config": {
|
||||
"backend": "webdav",
|
||||
"server": "webdav.example.com",
|
||||
"path": "/remote.php/webdav",
|
||||
"username": "user",
|
||||
"password": "${WEBDAV_PASSWORD}",
|
||||
"port": 80,
|
||||
"readOnly": false,
|
||||
"ssl": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Repository types
|
||||
|
||||
#### Local
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "local-repo",
|
||||
"config": {
|
||||
"backend": "local",
|
||||
"path": "/var/lib/zerobyte/repositories"
|
||||
},
|
||||
"compressionMode": "auto"
|
||||
}
|
||||
```
|
||||
|
||||
Note for importing existing local repositories (migration):
|
||||
|
||||
- include `config.name` and set `config.isExistingRepository: true`
|
||||
- the actual restic repo is stored at `{path}/{name}`
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-local-repo",
|
||||
"config": {
|
||||
"backend": "local",
|
||||
"path": "/var/lib/zerobyte/repositories",
|
||||
"name": "abc123",
|
||||
"isExistingRepository": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### S3-compatible
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "backup-repo",
|
||||
"config": {
|
||||
"backend": "s3",
|
||||
"bucket": "mybucket",
|
||||
"accessKeyId": "${ACCESS_KEY_ID}",
|
||||
"secretAccessKey": "${SECRET_ACCESS_KEY}"
|
||||
},
|
||||
"compressionMode": "auto"
|
||||
}
|
||||
```
|
||||
|
||||
#### Google Cloud Storage
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "gcs-repo",
|
||||
"config": {
|
||||
"backend": "gcs",
|
||||
"bucket": "mybucket",
|
||||
"projectId": "my-gcp-project",
|
||||
"credentialsJson": "${GCS_CREDENTIALS}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Azure Blob Storage
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "azure-repo",
|
||||
"config": {
|
||||
"backend": "azure",
|
||||
"container": "mycontainer",
|
||||
"accountName": "myaccount",
|
||||
"accountKey": "${AZURE_KEY}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Backup schedules
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "local-volume-local-repo",
|
||||
"volume": "local-volume",
|
||||
"repository": "local-repo",
|
||||
"cronExpression": "0 2 * * *",
|
||||
"retentionPolicy": { "keepLast": 7, "keepDaily": 7 },
|
||||
"includePatterns": ["important-folder"],
|
||||
"excludePatterns": ["*.tmp", "*.log"],
|
||||
"enabled": true,
|
||||
"notifications": ["slack-alerts", "email-admin"]
|
||||
}
|
||||
```
|
||||
|
||||
`notifications` can also be an array of objects:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name": "slack-alerts",
|
||||
"notifyOnStart": false,
|
||||
"notifyOnSuccess": true,
|
||||
"notifyOnWarning": true,
|
||||
"notifyOnFailure": true
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### User setup (automated)
|
||||
|
||||
Zerobyte currently supports a **single user**.
|
||||
If multiple entries are provided in `users[]`, only the first one will be applied.
|
||||
|
||||
New instance:
|
||||
|
||||
```json
|
||||
{
|
||||
"recoveryKey": "${RECOVERY_KEY}",
|
||||
"users": [
|
||||
{
|
||||
"username": "my-user",
|
||||
"password": "${ADMIN_PASSWORD}"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Migration:
|
||||
|
||||
```json
|
||||
{
|
||||
"recoveryKey": "${RECOVERY_KEY}",
|
||||
"users": [
|
||||
{
|
||||
"username": "my-user",
|
||||
"passwordHash": "$argon2id$v=19$m=19456,t=2,p=1$..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Use either `password` OR `passwordHash`, not both.
|
||||
|
||||
### Recovery key
|
||||
|
||||
The recovery key is a 64-character hex string that serves two critical purposes:
|
||||
|
||||
1. Restic repository password (encrypts your backup data)
|
||||
2. Database encryption key (encrypts credentials stored in Zerobyte)
|
||||
|
||||
Generating a recovery key ahead of time:
|
||||
|
||||
```bash
|
||||
# Using OpenSSL (Linux/macOS)
|
||||
openssl rand -hex 32
|
||||
|
||||
# Using Python
|
||||
python3 -c "import secrets; print(secrets.token_hex(32))"
|
||||
|
||||
# Using Docker (prints the key, container is removed)
|
||||
docker run --rm python:3.12-alpine sh -lc 'echo "Key is on the next line:"; python -c "import secrets; print(secrets.token_hex(32))"'
|
||||
```
|
||||
22
examples/config-file-import/docker-compose.yml
Normal file
22
examples/config-file-import/docker-compose.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
services:
|
||||
zerobyte:
|
||||
image: ghcr.io/nicotsx/zerobyte:latest
|
||||
container_name: zerobyte
|
||||
restart: unless-stopped
|
||||
cap_add:
|
||||
- SYS_ADMIN
|
||||
devices:
|
||||
- /dev/fuse:/dev/fuse
|
||||
ports:
|
||||
- "4096:4096"
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- TZ=${TZ:-UTC}
|
||||
- ZEROBYTE_CONFIG_IMPORT=true
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /var/lib/zerobyte:/var/lib/zerobyte
|
||||
- ./zerobyte.config.json:/app/zerobyte.config.json:ro
|
||||
- ./mydata:/mydata:ro
|
||||
- ~/.config/rclone:/root/.config/rclone
|
||||
Loading…
Reference in a new issue