Manifests

The manifest is the local state of an instance on the host. It holds everything DeployAlly needs to redeploy, update, or recover the service without losing configuration or secrets.

Each instance has its own directory at:

/opt/deployally/<instance_uid>/

This directory is the home of the instance: it persists across redeploys, is local to the host (never versioned in git), and contains only what belongs to that instance.

Structure

/opt/deployally/<instance_uid>/
├── state.yaml              # Instance metadata
├── secrets/                # Persisted secrets (mode 0700)
│   ├── MARIADB_ROOT_PASSWORD     # 0600
│   ├── MARIADB_PASSWORD          # 0600
│   └── WP_DB_PASSWORD            # 0600
└── config/                 # Rendered config files (bind-mount)
    ├── custom.cnf
    └── nginx.conf

`state.yaml`

Instance metadata — template reference, applied inputs, chosen profile, IDs of created containers and volumes, timestamps:

schema_version: "3"

instance:
  uid: memos-blog-001
  species: memos
  created_at: "2026-06-05T10:00:00Z"
  updated_at: "2026-06-05T10:00:00Z"

template:
  spec: "3"
  app_version: "0.22.0"
  archetype: application
  variant: stable

inputs:
  WEB_HOSTNAME: memos.example.com
  MEMOS_MODE: prod

profile: production

container:
  id: "a1b2c3d4e5f6…"
  name: memos-blog-001
  image: "neosmemo/memos:0.22.0"

storage:
  - id: data
    kind: volume
    docker_volume: memos-blog-001_data
    mount: /var/opt/memos

needs:
  database:
    asset_uid: mariadb-shared
    provisioned:
      database: wp_memos-blog-001
      user: wp_memos-blog-001

state:
  status: running
  health: healthy
  last_check: "2026-06-05T10:05:00Z"

`secrets/`

Each secret declared in the template takes one file, named after the secret with permission 0600. The secrets/ directory has permission 0700.

secrets/MARIADB_ROOT_PASSWORD   →   p4ssw0rd_s3cr3t0_d0_r00t
secrets/WP_DB_PASSWORD          →   aXc92kf…

When the template has secrets.X.persist: true, the secret is written on the first deploy and reused on subsequent redeploys. This guarantees that the database password doesn't change across updates.

`config/`

When the template has storage with kind: config_file, the file is rendered here and bind-mounted into the container. Each redeploy regenerates the file from the template (resolving ${input.X}, ${secrets.X}, etc.) — so configuration changes propagate automatically.

storage:
  - id: my-cnf
    kind: config_file
    mount: /etc/mysql/conf.d/custom.cnf
    content: |
      [mysqld]
      max_connections = ${input.MAX_CONNECTIONS}

Result on the host: /opt/deployally/<uid>/config/custom.cnf (mode 0644), mounted read-only at /etc/mysql/conf.d/custom.cnf inside the container.

Why this matters

1. Redeploy is idempotent

Running deployally deploy --species memos --instance-uid memos-blog-001 --apply twice yields the same container, with the same secrets and the same volumes. The manifest is the source of truth.

2. Recovery without losing data

If the container disappears (accident, cleanup, host crash), state.yaml lets you reconstruct everything: the Docker volume keeps its name, secrets stay on disk, inputs are recorded. Just redeploy.

3. Isolated multi-tenant

Each instance has its own directory. Ten WordPress instances on a shared MariaDB mean ten distinct /opt/deployally/wp-* directories, each with its own DB password in secrets/WP_DB_PASSWORD.

4. Auditability

state.yaml records which app_version, which variant, and which inputs produced that container. Who deployed, when, with what config — all traceable without inspecting the container.

Permissions

Path Mode Owner
/opt/deployally/<uid>/ 0755 root
state.yaml 0644 root
secrets/ 0700 root
secrets/<NAME> 0600 root
config/ 0755 root
config/<file> 0644 root

The container process runs as the user defined by the image; bind-mounts are read-only by default.

What's NOT in the manifest

  • Not versioned in git. The /opt/deployally/ directory is local to the host and contains secrets — never commit it.
  • Not stored elsewhere. DeployAlly does not sync manifests to the cloud by default.
  • No remote copy of secrets. If you lose the host disk without backup, the secrets are gone.

Backup

Backing up /opt/deployally/ is the operator's responsibility. Recommended:

# Backup all manifests on the host
tar -czf deployally-manifests-$(date +%F).tar.gz /opt/deployally/

# Restore
tar -xzf deployally-manifests-YYYY-MM-DD.tar.gz -C /

BackupAlly (a component of the CCS ecosystem) does this automatically when configured, honoring the backup tags declared on each template's storage (backup.tag: critical, backup.frequency: hourly, backup.retain: 168).

Next Steps

  • Definitions and Instances — How inputs become secrets and the manifest is populated.
  • Templates — How the template declares storage, secrets, and config.
  • Ecology — How multi-tenant instances share assets.
By Borlot.com.br on 05/06/2026