Concepts

Templates

Create a ready-to-deploy project from a catalog template.

A project template is a small directory of ordinary files (a compose file, an ant.yaml, an .env.example) plus a template.yaml that declares its metadata and the values a user can fill in. ant nest new renders one into a new project folder; the dashboard's Add project → Template does the same through the shared library.

ant nest templates sync                          # import the Dokploy catalog
ant nest templates list                          # (--json)
ant nest templates search postgres               # (--tag, --refresh, --limit, --json)
ant nest templates show ackee                    # metadata and variables (--json)
ant nest new ackee                               # → ~/.ant-apps/ackee
ant nest new ackee ~/stacks/ackee --group apps   # choose folder and group
ant nest new ackee --var HOST_PORT=5433          # set a template variable

ant nest new also takes --app, --target-machine, --force, and --json; templates sync takes --repo, --ref, and --dir (convert a local checkout instead of cloning).

The catalog is what you import with ant nest templates sync (the Dokploy blueprints, below) plus any templates in ~/.ant/templates; listing downloads nothing.

The Dokploy catalog

ant nest templates sync imports the Dokploy open-source blueprints (Dokploy/templates, several hundred stacks) as ant templates:

ant nest templates search postgres       # search Dokploy's catalog (cached 24h)
ant nest templates search --tag database
ant nest templates add plausible         # fetch one template
ant nest new plausible                   # fetches it on demand if not local
ant nest templates sync                  # or import the whole catalog (offline use)

search reads Dokploy's published catalog; new <id> (and templates add) downloads just that blueprint, converts it, and caches it under ~/.ant/templates-dokploy. You do not need a full sync for one stack. The dashboard's Add → Template does the same: it lists the whole catalog and filters it as you type, and downloads a template when you pick it. The catalog is bundled with ant and cached after a fetch, so the list still works when the index cannot be reached.

They land in ~/.ant/templates-dokploy and are listed with your own ~/.ant/templates; your own template overrides an imported one with the same id.

The conversion adapts Dokploy's model to ant's:

  • Ports. Dokploy routes through a shared Traefik on the compose network and services only expose their port. ant's Caddy routes to a host port, so each routed service gets a published loopback port ("3000:3000"); change it if it clashes with something else on the machine.
  • Variables. Dokploy ${password} values become generated secrets written to the project's .env; other values become defaults.
  • Domains. [[config.domains]] become ant deployment domains (a local <app>.localhost by default; add a real domain for a nest).
  • Config files. [[config.mounts]] are written under files/ and the compose's ../files/… binds are pointed at them; the loaded file is gitignored because it may hold a generated secret.
  • Env & secrets. [config.env] entries (table or env = […] array) are applied to the stack; a ${password} becomes a generated secret variable and ${domain} the local host. Values that reference other variables (https://${host}) resolve at instantiation; secrets are never inlined into the compose.
  • Swarm deploy:. Compose-honored keys (replicas, resources) are kept, restart_policy becomes restart, and Swarm-only keys (mode, placement, update/rollback config) are dropped. A top-level name: and per-service container_name: are removed, since ant namespaces per project.

Blueprints ant cannot represent (for example a Traefik label with a {{...}} template, or anything ant's template loader rejects) are skipped, so a bad blueprint never breaks the catalog. Values that Dokploy composes from other variables (a URL built from a password) may need a small edit after creation.

Where projects are created

Without a directory argument, a new project goes to ~/.ant-apps/<app>, or /etc/ant/apps/<app> when ant runs as root (e.g. on a server). Set ANT_APPS_DIR to override the root. The directory is created on the machine running ant: --target-machine records a deploy target in ant.yaml; it does not move the files.

Generated values

Secrets are generated per project and written to the project's .env file with mode 0600, alongside a .gitignore that keeps it out of git. Compose reads that file for ${VAR} interpolation, and ant injects it for container templates that declare env_file: [.env]. The CLI and the dashboard show generated values once, at creation time.

Databases are marked as such

A template sets the project's kind:

# ant.yaml
app: postgres
kind: database

The dashboard presents a database project without the source-build surfaces (made-up Dockerfile fields, image-keep counts) while keeping every deploy feature (environments, domains, volumes, resources) available. A project without kind is an ordinary app.

Authoring a template

Place a directory in ~/.ant/templates/<id>/. It overrides an imported template with the same id. The layout:

~/.ant/templates/my-db/
  template.yaml        # metadata + variable schema (never copied)
  docker-compose.yml   # rendered into the project
  ant.yaml             # rendered
  .env.example         # rendered

template.yaml:

name: My DB
description: A database with a generated password.
kind: database          # or app
app: mydb               # default app/folder name
run: compose            # or container
tags: [database, postgres]
port: 5432              # default port, recorded on the project
resolve: true           # resolve {{VAR}} in files at instantiation
variables:
  - key: MYDB_USER
    label: Database user
    default: app
    required: false
  - key: HOST_PORT
    type: port
    default: "5432"
  - key: MYDB_PASSWORD
    generate: password  # generated at instantiation
    secret: true        # written only to .env

A variable's type may be port (validated as a port) or a plain string; label, required, and secret refine how it is prompted and rendered.

Two substitution syntaxes coexist:

SyntaxResolvedUse for
{{KEY}}at instantiation, inlined into the fileports, names, ant.yaml fields
${KEY}at deploy time, from the generated .envsecrets and any value a user may rotate

Templates are validated when they load (and by go test ./internal/template):

  • the template id must match ^[a-z0-9][a-z0-9._-]*$;
  • ant.yaml is required and must set deploy.build: none; templates run pre-built images;
  • run: must be container or compose;
  • the compose file must not use container_name, a top-level name:, or external: true networks/volumes; compose's per-project namespace (-p <app>-<env>) already keeps stacks apart;
  • .env is reserved for ant's generated file, so ship .env.example instead;
  • a secret variable must use ${KEY}, never {{KEY}}, and must also set generate:;
  • every declared variable must be referenced somewhere;
  • the default render must parse and load.

See Deploy for what the generated ant.yaml can do.

Copyright © 2026