---
title: Provisioned Resources
description: Manage repositories and volumes through a configuration file with secret references
---

import { Step, Steps } from "fumadocs-ui/components/steps";
import { Callout } from "fumadocs-ui/components/callout";

Zerobyte can sync operator-managed repositories and volumes from a JSON configuration file at startup. This is useful when you want credentials and connection details to live in deployment-time configuration instead of being entered through the UI.

## What Provisioning Supports Today

Provisioning currently supports:

- repositories
- volumes
- secret references through `env://` and `file://`
- updating managed resources in place by keeping the same `id`
- deleting managed resources with `"delete": true`

Provisioning does **not** currently support:

- backup jobs or schedules
- notification destinations
- exporting UI-created resources back into provisioning JSON
- live reloading without restarting the Zerobyte container

<Callout type="warn">
	Provisioning is limited to repositories and volumes in schema version `1`. If you need to manage backup jobs or
	notifications, create them through the UI or API for now.
</Callout>

## Prerequisites

- A running Zerobyte instance with an organization ID
- The `PROVISIONING_PATH` environment variable pointing to your JSON file

### Find your organization ID

1. Sign in to Zerobyte and switch to the organization you want to provision into
2. Open **Settings**
3. Open the **Organization** tab
4. Copy the read-only **Organization ID** value from **Organization Details**

<Callout type="info">
	The **Organization** tab is only visible to organization `admin` and `owner` members. If you do not see it, ask an
	organization admin or owner for the ID, or have them grant you the required role first.
</Callout>

## Quick Start

<Steps>
<Step>

### Create the provisioning file

Create a `provisioning.json` file:

```json provisioning.json
{
	"version": 1,
	"repositories": [
		{
			"id": "local-repo",
			"organizationId": "your-organization-id",
			"name": "Primary Local Repository",
			"backend": "local",
			"compressionMode": "auto",
			"config": {
				"backend": "local",
				"path": "/var/lib/zerobyte/repositories/primary",
				"isExistingRepository": false
			}
		}
	],
	"volumes": [
		{
			"id": "documents",
			"organizationId": "your-organization-id",
			"name": "Documents",
			"backend": "directory",
			"autoRemount": true,
			"config": {
				"backend": "directory",
				"path": "/data/documents"
			}
		}
	]
}
```

</Step>
<Step>

### Mount the file and configure Zerobyte

Mount the provisioning file and set `PROVISIONING_PATH`:

```yaml compose.yaml
services:
  zerobyte:
    image: ghcr.io/nicotsx/zerobyte:v0.39
    environment:
      - PROVISIONING_PATH=/config/provisioning.json
    volumes:
      - ./provisioning.json:/config/provisioning.json:ro
      - /var/lib/zerobyte:/var/lib/zerobyte
      - /srv/documents:/data/documents
```

</Step>
<Step>

### Start or restart Zerobyte

```bash
docker compose up -d
```

On startup, Zerobyte reads the provisioning file, resolves supported secret references, encrypts the resolved secret values, and syncs the managed resources into the database.

</Step>
</Steps>

## Root File Format

The root object always has the same shape:

```json
{
	"version": 1,
	"repositories": [],
	"volumes": []
}
```

| Field          | Required | Notes                                               |
| -------------- | -------- | --------------------------------------------------- |
| `version`      | yes      | Must be `1`                                         |
| `repositories` | no       | Array of provisioned repositories. Defaults to `[]` |
| `volumes`      | no       | Array of provisioned volumes. Defaults to `[]`      |

## Repository Entry Reference

Each item in `repositories` uses this top-level shape:

```json
{
	"id": "repo-id",
	"organizationId": "org-id",
	"name": "Repository Name",
	"backend": "local",
	"compressionMode": "auto",
	"delete": false,
	"config": {
		"backend": "local",
		"path": "/var/lib/zerobyte/repositories/repo-id"
	}
}
```

| Field             | Required | Notes                                                                                                                              |
| ----------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `id`              | yes      | Stable provisioning identifier inside the organization. Keep this stable if you want Zerobyte to update the same resource in place |
| `organizationId`  | yes      | Must match an existing organization                                                                                                |
| `name`            | yes      | Display name in Zerobyte                                                                                                           |
| `backend`         | yes      | Must be one of `local`, `s3`, `r2`, `gcs`, `azure`, `rclone`, `rest`, `sftp`                                                       |
| `compressionMode` | no       | `off`, `auto`, or `max`                                                                                                            |
| `delete`          | no       | Defaults to `false`                                                                                                                |
| `config`          | yes      | Backend-specific object. `config.backend` must match the top-level `backend` value                                                 |

### Repository Shared Config Fields

These fields are available inside every repository `config` object:

| Field                  | Required | Notes                                                                                                                                                                               |
| ---------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `isExistingRepository` | no       | When `true`, Zerobyte treats the repository as already initialized. When omitted or `false`, a newly created provisioned repository is initialized with `restic init` on first sync |
| `customPassword`       | no       | Overrides the organization-level repository password                                                                                                                                |
| `cacert`               | no       | Custom CA certificate contents                                                                                                                                                      |
| `insecureTls`          | no       | Disables TLS verification for supported backends                                                                                                                                    |
| `uploadLimit`          | no       | Bandwidth limit object                                                                                                                                                              |
| `downloadLimit`        | no       | Bandwidth limit object                                                                                                                                                              |

Bandwidth limit objects use this shape:

```json
{
	"enabled": true,
	"value": 10,
	"unit": "Mbps"
}
```

`unit` must be one of `Kbps`, `Mbps`, or `Gbps`.

### Repository Backends

<Tabs items={["Local", "S3", "R2", "GCS", "Azure", "Rclone", "REST", "SFTP"]}>
  <Tab value="Local">
    | Field  | Required | Notes                              |
    | ------ | -------- | ---------------------------------- |
    | `path` | yes      | Path inside the Zerobyte container |

    ```json
    {
    	"backend": "local",
    	"path": "/var/lib/zerobyte/repositories/primary",
    	"isExistingRepository": false
    }
    ```

  </Tab>

  <Tab value="S3">
    | Field             | Required | Notes                               |
    | ----------------- | -------- | ----------------------------------- |
    | `endpoint`        | yes      | Example: `https://s3.amazonaws.com` |
    | `bucket`          | yes      | Bucket name                         |
    | `accessKeyId`     | yes      | Supports secret references          |
    | `secretAccessKey` | yes      | Supports secret references          |

    ```json
    {
    	"backend": "s3",
    	"endpoint": "https://s3.amazonaws.com",
    	"bucket": "company-backups",
    	"accessKeyId": "env://AWS_ACCESS_KEY_ID",
    	"secretAccessKey": "file://aws_secret_access_key",
    	"isExistingRepository": true
    }
    ```

  </Tab>

  <Tab value="R2">
    | Field             | Required | Notes                          |
    | ----------------- | -------- | ------------------------------ |
    | `endpoint`        | yes      | Your Cloudflare R2 S3 endpoint |
    | `bucket`          | yes      | Bucket name                    |
    | `accessKeyId`     | yes      | Supports secret references     |
    | `secretAccessKey` | yes      | Supports secret references     |

    ```json
    {
    	"backend": "r2",
    	"endpoint": "https://<account-id>.r2.cloudflarestorage.com",
    	"bucket": "zerobyte-backups",
    	"accessKeyId": "env://R2_ACCESS_KEY_ID",
    	"secretAccessKey": "env://R2_SECRET_ACCESS_KEY",
    	"isExistingRepository": true
    }
    ```

  </Tab>

  <Tab value="GCS">
    | Field             | Required | Notes                                            |
    | ----------------- | -------- | ------------------------------------------------ |
    | `bucket`          | yes      | Bucket name                                      |
    | `projectId`       | yes      | Google Cloud project ID                          |
    | `credentialsJson` | yes      | Service account JSON. Supports secret references |

    ```json
    {
    	"backend": "gcs",
    	"bucket": "zerobyte-backups",
    	"projectId": "my-gcp-project",
    	"credentialsJson": "file://gcs_credentials_json",
    	"isExistingRepository": true
    }
    ```

  </Tab>

  <Tab value="Azure">
    | Field            | Required | Notes                      |
    | ---------------- | -------- | -------------------------- |
    | `container`      | yes      | Blob container name        |
    | `accountName`    | yes      | Storage account name       |
    | `accountKey`     | yes      | Supports secret references |
    | `endpointSuffix` | no       | Custom endpoint suffix     |

    ```json
    {
    	"backend": "azure",
    	"container": "zerobyte-backups",
    	"accountName": "storageaccount",
    	"accountKey": "env://AZURE_STORAGE_ACCOUNT_KEY",
    	"endpointSuffix": "core.windows.net",
    	"isExistingRepository": true
    }
    ```

  </Tab>

  <Tab value="Rclone">
    | Field    | Required | Notes                                |
    | -------- | -------- | ------------------------------------ |
    | `remote` | yes      | Name of the configured rclone remote |
    | `path`   | yes      | Path inside that remote              |

    ```json
    {
    	"backend": "rclone",
    	"remote": "remote-name",
    	"path": "zerobyte/backups",
    	"isExistingRepository": true
    }
    ```

  </Tab>

  <Tab value="REST">
    | Field      | Required | Notes                                 |
    | ---------- | -------- | ------------------------------------- |
    | `url`      | yes      | Base REST server URL                  |
    | `username` | no       | Supports secret references            |
    | `password` | no       | Supports secret references            |
    | `path`     | no       | Optional path below the REST endpoint |

    ```json
    {
    	"backend": "rest",
    	"url": "https://rest-server.example.com",
    	"username": "env://REST_USERNAME",
    	"password": "file://rest_password",
    	"path": "zerobyte",
    	"isExistingRepository": true
    }
    ```

  </Tab>

  <Tab value="SFTP">
    | Field              | Required | Notes                                            |
    | ------------------ | -------- | ------------------------------------------------ |
    | `host`             | yes      | SFTP hostname                                    |
    | `port`             | no       | Defaults to `22`                                 |
    | `user`             | yes      | SSH username                                     |
    | `path`             | yes      | Remote repository path                           |
    | `privateKey`       | yes      | Private key contents. Supports secret references |
    | `skipHostKeyCheck` | no       | Defaults to `false`                              |
    | `knownHosts`       | no       | Contents of a known hosts file                   |

    ```json
    {
    	"backend": "sftp",
    	"host": "backup.example.com",
    	"port": 22,
    	"user": "backup",
    	"path": "/srv/restic",
    	"privateKey": "file://sftp_private_key",
    	"skipHostKeyCheck": false,
    	"knownHosts": "backup.example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI..."
    }
    ```

  </Tab>
</Tabs>

## Volume Entry Reference

Each item in `volumes` uses this top-level shape:

```json
{
	"id": "volume-id",
	"organizationId": "org-id",
	"name": "Volume Name",
	"backend": "directory",
	"autoRemount": true,
	"delete": false,
	"config": {
		"backend": "directory",
		"path": "/data/volume"
	}
}
```

| Field            | Required | Notes                                                                              |
| ---------------- | -------- | ---------------------------------------------------------------------------------- |
| `id`             | yes      | Stable provisioning identifier inside the organization                             |
| `organizationId` | yes      | Must match an existing organization                                                |
| `name`           | yes      | Display name in Zerobyte                                                           |
| `backend`        | yes      | Must be one of `nfs`, `smb`, `directory`, `webdav`, `rclone`, `sftp`               |
| `autoRemount`    | no       | Defaults to `true`                                                                 |
| `delete`         | no       | Defaults to `false`                                                                |
| `config`         | yes      | Backend-specific object. `config.backend` must match the top-level `backend` value |

### Volume Backends

<Tabs items={["Directory", "NFS", "SMB/CIFS", "WebDAV", "SFTP", "Rclone"]}>
  <Tab value="Directory">
    | Field      | Required | Notes                              |
    | ---------- | -------- | ---------------------------------- |
    | `path`     | yes      | Path inside the Zerobyte container |
    | `readOnly` | no       | If provided, it must be `false`    |

    ```json
    {
    	"backend": "directory",
    	"path": "/data/documents"
    }
    ```

  </Tab>

  <Tab value="NFS">
    | Field        | Required | Notes                       |
    | ------------ | -------- | --------------------------- |
    | `server`     | yes      | NFS server hostname or IP   |
    | `exportPath` | yes      | Exported path on the server |
    | `port`       | no       | Defaults to `2049`          |
    | `version`    | yes      | Must be `3`, `4`, or `4.1`  |
    | `readOnly`   | no       | Mount the volume read-only  |

    ```json
    {
    	"backend": "nfs",
    	"server": "10.0.0.10",
    	"exportPath": "/exports/media",
    	"port": 2049,
    	"version": "4.1",
    	"readOnly": true
    }
    ```

  </Tab>

  <Tab value="SMB/CIFS">
    | Field      | Required | Notes                                                     |
    | ---------- | -------- | --------------------------------------------------------- |
    | `server`   | yes      | SMB server hostname or IP                                 |
    | `share`    | yes      | Share name                                                |
    | `username` | no       | Login username                                            |
    | `password` | no       | Supports secret references                                |
    | `guest`    | no       | Use guest authentication                                  |
    | `vers`     | no       | `1.0`, `2.0`, `2.1`, `3.0`, or `auto`. Defaults to `auto` |
    | `domain`   | no       | SMB domain or workgroup                                   |
    | `port`     | no       | Defaults to `445`                                         |
    | `readOnly` | no       | Mount the volume read-only                                |

    ```json
    {
    	"backend": "smb",
    	"server": "fileserver.local",
    	"share": "team",
    	"username": "backup-user",
    	"password": "env://SMB_PASSWORD",
    	"vers": "3.0",
    	"domain": "WORKGROUP",
    	"port": 445,
    	"readOnly": false
    }
    ```

  </Tab>

  <Tab value="WebDAV">
    | Field      | Required | Notes                           |
    | ---------- | -------- | ------------------------------- |
    | `server`   | yes      | WebDAV hostname, without scheme |
    | `path`     | yes      | Remote path                     |
    | `username` | no       | Login username                  |
    | `password` | no       | Supports secret references      |
    | `port`     | no       | Defaults to `80`                |
    | `readOnly` | no       | Mount the volume read-only      |
    | `ssl`      | no       | Enable HTTPS                    |

    ```json
    {
    	"backend": "webdav",
    	"server": "cloud.example.com",
    	"path": "/team-a",
    	"username": "team-a",
    	"password": "env://WEBDAV_PASSWORD",
    	"port": 443,
    	"ssl": true
    }
    ```

  </Tab>

  <Tab value="SFTP">
    | Field              | Required | Notes                                            |
    | ------------------ | -------- | ------------------------------------------------ |
    | `host`             | yes      | SFTP hostname                                    |
    | `port`             | no       | Defaults to `22`                                 |
    | `username`         | yes      | SSH username                                     |
    | `password`         | no       | Supports secret references                       |
    | `privateKey`       | no       | Private key contents. Supports secret references |
    | `path`             | yes      | Remote path                                      |
    | `readOnly`         | no       | Mount the volume read-only                       |
    | `skipHostKeyCheck` | no       | Defaults to `false`                              |
    | `knownHosts`       | no       | Contents of a known hosts file                   |

    ```json
    {
    	"backend": "sftp",
    	"host": "files.example.com",
    	"port": 22,
    	"username": "backup-user",
    	"privateKey": "file://volume_sftp_private_key",
    	"path": "/srv/data",
    	"readOnly": true,
    	"skipHostKeyCheck": false,
    	"knownHosts": "files.example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI..."
    }
    ```

  </Tab>

  <Tab value="Rclone">
    | Field      | Required | Notes                                |
    | ---------- | -------- | ------------------------------------ |
    | `remote`   | yes      | Name of the configured rclone remote |
    | `path`     | yes      | Path inside that remote              |
    | `readOnly` | no       | Mount the volume read-only           |

    ```json
    {
    	"backend": "rclone",
    	"remote": "remote-name",
    	"path": "team-a",
    	"readOnly": true
    }
    ```

  </Tab>
</Tabs>

## Secret References

Supported secret references use one of these prefixes:

| Reference             | Resolves From                   | Example                        |
| --------------------- | ------------------------------- | ------------------------------ |
| `env://VARIABLE_NAME` | Container environment variables | `env://AWS_ACCESS_KEY_ID`      |
| `file://secret_name`  | `/run/secrets/secret_name`      | `file://aws_secret_access_key` |

<Callout type="info">
	`file://` references always resolve from `/run/secrets/` and must be a single filename, not a nested path.
</Callout>

Only specific fields are resolved as secret references. Other string fields are treated literally.

### Repository fields that support secret references

- shared fields: `customPassword`, `cacert`
- `s3`: `accessKeyId`, `secretAccessKey`
- `r2`: `accessKeyId`, `secretAccessKey`
- `gcs`: `credentialsJson`
- `azure`: `accountKey`
- `rest`: `username`, `password`
- `sftp`: `privateKey`

### Volume fields that support secret references

- `smb`: `password`
- `webdav`: `password`
- `sftp`: `password`, `privateKey`

Resolved secret values are encrypted before Zerobyte stores them in the database.

## Rotating Secrets

To rotate a provisioned secret:

1. Update the environment variable or secret file
2. Restart Zerobyte with `docker compose restart`

Zerobyte re-resolves supported secret references on each startup.

## Updating And Removing Managed Resources

- Keep the same `id` to update a managed resource in place
- Changing only `name` keeps the same underlying resource record
- Setting `"delete": true` removes the managed resource on the next startup sync

Deletion entries still need the normal entry shape today because the provisioning file is validated before Zerobyte applies the `delete` flag:

```json
{
	"id": "repo-to-remove",
	"organizationId": "your-organization-id",
	"name": "Repo to remove",
	"backend": "local",
	"delete": true,
	"config": {
		"backend": "local",
		"path": "/var/lib/zerobyte/repositories/old",
		"isExistingRepository": true
	}
}
```

## Troubleshooting

### `No matching discriminator`

This error usually means one of these is wrong:

- `backend` is not a valid value for that resource type
- `config.backend` does not match the top-level `backend`
- the `config` object does not match the required fields for that backend

### Secret reference errors

- `env://NAME` requires the environment variable to exist in the Zerobyte container
- `file://name` requires `/run/secrets/name` to exist
- Zerobyte stops syncing when it hits a provisioning error, so fix the first reported error and restart again

## Important Notes

<Callout type="warn">
	Each provisioned entry must reference an existing `organizationId`. Complete first-run setup before enabling
	provisioning so you have a valid organization ID.
</Callout>

- Changes to `provisioning.json` only apply on container restart
- Provisioned resources appear in the normal UI and are marked as managed
- Editing a provisioned resource in the UI is useful for testing, but the next provisioning sync can overwrite it

import { Tab, Tabs } from "fumadocs-ui/components/tabs";
