moved documentation into separate example sub directory

This commit is contained in:
Jakub Trávník 2025-12-21 14:06:37 +01:00
parent 8c3e85cb50
commit 7b2bb251fd
8 changed files with 442 additions and 420 deletions

397
README.md
View file

@ -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.

View file

@ -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

View file

@ -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

View 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=

View file

@ -0,0 +1,2 @@
.env
zerobyte.config.json

View 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 its 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 Zerobytes 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))"'
```

View 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