Skip to Content
DocsReferenceConfiguration

Configuration Reference

Complete reference for Jiji configuration files (.jiji/deploy.yml or jiji.<environment>.yml).

Configuration File Structure

Jiji uses YAML to define your infrastructure. The default file is .jiji/deploy.yml; you can also create environment-specific configs like jiji.staging.yml or jiji.production.yml.

# Use default .jiji/deploy.yml jiji deploy # Use environment-specific config jiji -e staging deploy # Uses jiji.staging.yml jiji -e production deploy # Uses jiji.production.yml # Use a custom config file jiji -c /path/to/custom.yml deploy

When no config file is specified, Jiji searches upward from the current directory for .jiji/deploy.yml (or jiji.{environment}.yml when -e/--environment is given), continuing up the directory tree until a config file is found or the filesystem root is reached.

Minimal Configuration

project: myapp builder: engine: docker registry: port: 31270 ssh: user: deploy servers: server1: host: server1.example.com services: web: image: nginx:latest servers: - server1 ports: - "80"

Project

project (required)

Unique identifier for your application. Used to namespace container names, audit logs, and deployment locks.

project: myapp

Builder

Controls how container images are built and where they’re stored.

builder: # Container engine (required): docker or podman engine: docker # Enable build cache (optional, default: true) cache: true # Remote builder SSH connection (optional) # Omit this field to build on the local machine. remote: ssh://builder@192.168.1.50:22

Local development:

builder: engine: docker cache: true

Remote building (offload to a dedicated build server):

builder: engine: docker remote: ssh://builder@build-server.example.com cache: true

If the configured engine (Docker or Podman) is not already installed on the remote builder host, jiji build installs it automatically. It also upgrades Podman when the installed version is older than the required 5.8.4. This is the same distro-aware installer jiji server setup uses for a deployment host, and jiji prints a status line confirming what it installed or upgraded.

On Debian and Ubuntu, the installer uses the pinned mgoltzsche/podman-static 5.8.4 bundle and verifies its archive checksum before installation. This bundle is an unofficial, single-maintainer Podman distribution. jiji doesn’t do anything else to a builder host beyond that: no network, WireGuard, or proxy setup, only the engine itself. Multi-architecture tooling (Buildx for Docker, manifest support for Podman) is not auto-installed - if your build needs more than one platform, install Buildx/manifest support on the builder yourself first; jiji only detects and reports whether it’s there. The build context is streamed over SSH into a staging directory on the builder, which is always cleaned up afterward, including when the build fails.

When builder.registry.server is set, Jiji uses a remote registry. Jiji logs in on the builder itself because that machine runs the push. When server is absent, Jiji opens a reverse SSH tunnel from the builder’s 127.0.0.1:<registry.port> to the local registry. This is the same fixed-port design that jiji deploy uses for a deployment host. See Local Registry Details for details. Two concurrent jiji build runs for the same project against the same builder will race for that port; the loser fails with an actionable message. jiji build has no deployment lock, so either serialize concurrent builds against one builder, or give each run its own builder.registry.port.

CI/CD (always fresh builds):

builder: engine: docker cache: false

Registry

Where built images are stored and pulled from, configured under builder.registry.

Local registry - a loopback registry with SSH reverse tunnels to deployment hosts, useful for development. jiji runs it on localhost:31270, tunnels it to each remote server for the duration of the deploy, and tears the tunnel down afterward.

builder: registry: port: 31270 # optional, defaults to 31270

Remote registry:

builder: registry: server: ghcr.io username: myuser password: GITHUB_TOKEN
RegistryserverAuto namespaceResult
GHCRghcr.iousernameghcr.io/username/project-service:version
Docker Hubdocker.iousernamedocker.io/username/project-service:version
Customregistry.example.com:5000noneregistry.example.com:5000/project-service:version

password can be a literal value, an ALL_CAPS secret name resolved from .env/host-env (see Environment Variables below), or $(a local command)

  • useful for registries like AWS ECR or GCP Artifact Registry that issue short-lived tokens, see Registry Reference.

SSH

ssh: user: deploy # required port: 22 # optional, default 22 connect_timeout: 30 # optional, seconds, default 30 command_timeout: 300 # optional, seconds, default 300

Authentication - ssh-agent is used by default if no keys are specified. Configure one or more identities with keys:

ssh: user: deploy keys: - ~/.ssh/id_ed25519 - /path/to/deploy_key

Each keys entry accepts a literal path or inline private key, an ALL_CAPS variable resolved from .env (or the host environment with --host-env), or $(a local command). Resolved values beginning with a private-key PEM header are used as inline key material; other values are treated as paths.

# Inline key material can use the same keys list ssh: user: deploy keys: - | -----BEGIN OPENSSH PRIVATE KEY----- ... -----END OPENSSH PRIVATE KEY-----
# Disable ssh-agent, use only the listed keys ssh: user: deploy keys_only: true keys: - ~/.ssh/deploy_key

Proxy / jump hosts

# ProxyJump ssh: user: deploy proxy: bastion.example.com # or deploy@bastion.example.com, or bastion.example.com:2222
# ProxyCommand, connect through an arbitrary command ssh: user: deploy proxy_command: "ssh -W %h:%p bastion.example.com"

proxy_command supports the %h/%p/%r/%% tokens (other OpenSSH tokens aren’t substituted) and is mutually exclusive with proxy on the same server. The command is spawned as a subprocess and its stdio becomes the SSH transport, matching real OpenSSH behavior.

SSH config file

ssh: user: deploy config: true # load ~/.ssh/config # or: config: ~/.ssh/custom_config # or: config: [~/.ssh/config, ~/.ssh/work_config]

Host-specific settings (wildcards supported), HostName/User/Port/ IdentityFile, ProxyJump/ProxyCommand, and ConnectTimeout/ IdentitiesOnly are all inherited. Explicit ssh: fields in deploy.yml take precedence over the SSH config file.

Advanced

ssh: user: deploy max_concurrent_starts: 30 # limit concurrent SSH connections pool_idle_timeout: 900 # seconds before idle connections close dns_retries: 3 # retry DNS lookups with backoff log_level: error # debug, info, warn, error, fatal options: # optional, raw SSH client options StrictHostKeyChecking: "no"

options: is a map of raw SSH client option names to values, applied alongside the structured ssh: fields above.

Network

Enables the private, encrypted WireGuard mesh with automatic .jiji service discovery. See Network Reference for the full design.

Most projects must omit network:. Jiji derives stable project-specific ranges from project:.

SettingDefaultDescription
enabledtrueEnable the private network
management_cidrProject-derived /24WireGuard host-address pool override
container_cidrProject-derived /16Routed container-address pool override
dns_forwarders1.1.1.1, 8.8.8.8Resolvers used for any query outside this project’s own .jiji zone

The .jiji domain is internal-only and not customizable - service discovery always resolves names like myapp-api.jiji.

Every deployed service container’s resolv.conf only ever has this project’s own agent as its nameserver, so a normal internet hostname (an API your app calls, a package registry, etc.) is resolved by forwarding the query to dns_forwarders rather than by the container also getting the host’s own DNS servers. Override this list to point at a home router, Pi-hole, or other local resolver instead of the public default.

The derived ranges depend only on project:, not the checkout directory. Jiji checks the host routes and the range markers of other projects before it changes the network. Use overrides for a LAN, VPN, cloud VPC, or rare project slot collision. See Network Reference.

# Avoid CIDR conflicts with an existing network network: management_cidr: "172.20.0.0/24" container_cidr: "172.21.0.0/16"
# Disable networking entirely network: enabled: false

jiji deploy, jiji service restart, and jiji service rollback require the private network. Use enabled: false only when the configuration will not run these service operations.

Service discovery example

servers: server1: host: server1.example.com server2: host: server2.example.com server3: host: server3.example.com services: api: servers: - server1 - server2 database: servers: - server3
# From the api container, connect to database DATABASE_URL: postgresql://user:pass@myapp-database.jiji:5432/myapp # From the database container, connect to api API_URL: http://myapp-api.jiji:3000 # Reach only the api replica on server1 API_SERVER1_URL: http://myapp-api-server1.jiji:3000

Servers

servers: app-amd: host: app-amd.example.com arch: amd64 app-arm: host: app-arm.example.com arch: arm64 user: ubuntu port: 2222 keys: - ~/.ssh/app-arm

host is required and is the SSH destination unless an enabled OpenSSH configuration supplies a matching HostName. -H/--hosts filters match both the config key name (app-arm) and the configured host value (app-arm.example.com).

arch is optional and defaults to amd64. The supported values are amd64 and arm64. When a service targets servers with both architectures, Jiji builds it for both linux/amd64 and linux/arm64. Multi-platform images must be pushed, so jiji build --no-push is unavailable for that service.

Each server can override these top-level ssh: defaults:

FieldResolution order
userserver, top-level ssh.user, matching OpenSSH User
portserver, non-default ssh.port, matching OpenSSH Port, 22
keysserver keys, top-level ssh.keys, matching OpenSSH IdentityFile
key_passphraseserver, top-level ssh.key_passphrase

keys is always a list, including when only one identity is configured. The former singular key_path field has been removed. Other SSH options, including timeouts, proxy settings, keys_only, DNS retries, and pool limits, are global. key_passphrase is a literal value, it is not resolved from .env or the host environment.

Services

Each service is a deployable unit - a containerized application, either from a pre-built image or built from source.

services: web: image: nginx:latest # pre-built image servers: - server1 # required: target servers - server2 ports: # optional - "80" - "443"

Image names are normalized before Docker or Podman operations. nginx:latest becomes docker.io/library/nginx:latest, and owner/image:tag becomes docker.io/owner/image:tag. Already-qualified registries such as ghcr.io/owner/image, localhost:5000/image, and private registry hostnames remain unchanged.

Servers and scale

services: web: image: nginx:latest servers: - server1 # literal deploy target list - server2 - server3 scale: 2 # optional, default: 1

servers: is the literal deploy target list: every listed server gets a deployment, always. scale: is the instance count on each listed server, not a total across them — scale: 2 here runs 2 on server1, 2 on server2, and 2 on server3 (6 total), not 2 total. Change the runtime count without editing config with jiji service scale N -S web (or --reset to return to the configured value).

Scheduled jobs (crons)

Each service can define multiple scheduled commands. The map key is the stable job name.

services: worker: image: ghcr.io/example/worker:latest servers: - server1 - server2 environment: secrets: - DATABASE_URL crons: sync-reports: schedule: "7 */2 * * *" command: ["npm", "run", "sync:reports"] remove-expired: schedule: "0 3 * * *" command: ["npm", "run", "remove-expired"] timezone: America/Denver timeout: 30m overlap: forbid missed_runs: skip
SettingDefaultDescription
schedulerequiredFive-field cron expression: minute, hour, day of month, month, day of week
commandrequiredCommand string or argument list for the one-off container
timezoneUTCIANA time-zone name
timeout1hMaximum run time, with an s, m, or h suffix
overlapforbidSkip a due run while the prior run remains active
missed_runsskipDo not replay times missed while the owning agent was offline

Each run uses the service image, environment, secrets, mounts, resources, project network, and .jiji DNS. It does not use the service command, ports, proxy routes, health checks, or restart policy.

Run jiji deploy after a cron configuration change. See Scheduled Jobs for ownership, failure behavior, retention, and commands.

Build configuration

Use build: instead of image: to build from source (only one of the two is allowed per service). A bare string is shorthand for context::

services: web: build: ./web # shorthand for build: { context: ./web }
services: web: build: context: . # optional, default: . dockerfile: Dockerfile # optional, default: Dockerfile target: production # optional, build target for multi-stage Dockerfiles args: NODE_ENV: production VERSION: 1.2.3 servers: - server1

context: can be omitted entirely from the detailed form - it defaults to the project root, same as leaving it out of the shorthand:

services: site: build: dockerfile: Dockerfile # context defaults to the project root servers: - server1

args: is a mapping of build-arg names to values, not a list of KEY=value strings.

Secret values (API tokens, private registry credentials) don’t belong in args: - a build argument ends up readable in docker history and image metadata forever. Use secrets: instead, which mounts the value into the build via Docker/Podman’s --secret flag and never writes it into the image:

services: api-backend: build: context: ./api secrets: - NPM_TOKEN - PIP_INDEX_PASSWORD

Each name is resolved the same way environment.secrets names are - from the selected .env file, then the host environment if --host-env is passed - but never from environment.clear: a build secret exists specifically to bypass the cleartext build-arg path, so mixing in a cleartext source would defeat the point. The Dockerfile reads it as a mounted file, not an environment variable:

RUN --mount=type=secret,id=NPM_TOKEN \ NPM_TOKEN=$(cat /run/secrets/NPM_TOKEN) npm install

Classic (non-buildx) docker build only understands --secret when BuildKit is active; Jiji sets DOCKER_BUILDKIT=1 automatically for that one build invocation when secrets: is configured, so this needs no setup step from you. buildx build and podman build support --secret natively. Works for local builds, builder.remote, and multi-architecture builds - the CLI stages each secret to a mode-0600 temporary file (locally) or a mode-0600 file on the builder host (remotely, piped over SSH stdin, never embedded in a command string) and removes it once the build finishes, whether it succeeded or failed.

dockerfile: is resolved relative to context:, matching Docker Compose - if context: ./web, dockerfile: Dockerfile (the default) already means web/Dockerfile; there’s no need to repeat the web/ prefix. A dockerfile: value can still climb out of the context with ../ for local builds (the local engine reads the filesystem directly), but a remote builder can only stage what’s inside the configured context, so that combination is rejected there.

Port mappings

For services behind the proxy, specify only the container port - this lets the old and new container coexist during a deploy, since neither binds a host port:

ports: - "80" - "3000"

Binding a host port (host:container) means only one container can hold that port at a time, which rules out zero-downtime deploys for that service - pair it with stop_first: true (see Stateful services) or the candidate will simply fail to bind the port and the deploy will fail outright. Use this form only for services that need direct host port access without the proxy (databases, non-HTTP services):

ports: - "80:80" - "53/udp" # container port with protocol - "53:53/udp" # host:container with protocol - "127.0.0.1:8080:80" # bind to localhost only

Volume mounts

volumes: - "/data/web/logs:/var/log/nginx" - "web_storage:/opt/uploads" - "./data:/opt/extra_data:ro"

Note: Named volumes (those not starting with / or ./) are automatically prefixed with the service name to prevent conflicts between services. For example, if a service named web defines web_storage:/opt/uploads, the actual volume created is web-web_storage. Host path mounts are not modified.

File and directory mounts

# String format: local:remote[:options], options can be ro, z, or Z files: - "nginx.conf:/etc/nginx/nginx.conf:ro" directories: - "html:/usr/share/nginx/html:ro"

Or the hash format, for custom permissions and ownership:

files: - local: config/secret.key remote: /etc/app/secret.key mode: "0600" owner: "nginx:nginx" options: "ro"

Environment variables

Shared variables (project-level, applied to every service):

environment: clear: APP_ENV: production LOG_LEVEL: info secrets: - API_KEY - DATABASE_PASSWORD

Service-specific variables, merged with the shared set:

services: web: environment: clear: NODE_ENV: production PORT: 3000 secrets: - API_KEY

secrets entries are ALL_CAPS names resolved from .env files (or host environment variables with --host-env), never written literally into config. Numbers and booleans under clear are converted to strings for container compatibility (DEBUG: true becomes "true").

Resolved secrets are staged on the target server at .jiji/{project}/env/{service}-{server}.env, mode 0600, written over SSH so the value never appears as a command-line argument or in shell history. This file is not removed by jiji service remove; it persists (root-owned, mode 0600) until jiji server teardown clears the project’s .jiji/ directory.

Custom .env location

secrets_path: config/secrets # optional, default: .env

Jiji looks for {secrets_path}.{environment} first (e.g. config/secrets.production with -e production), then falls back to secrets_path itself. With the default secrets_path, that means .env.{environment} then .env.

External secrets adapters

The schema also accepts a top-level secrets: block describing an external adapter:

secrets: adapter: doppler project: myapp config: production

This currently parses but has no effect: no adapter implementation reads it yet, so configuring it changes nothing and produces no warning. Use .env-resolved secrets: entries under environment: (above), or a $(command) value for builder.registry.password (see Registry Reference), for anything that needs a secret today.

Proxy

Enables jiji-proxy routing for a service.

proxy: port: 3000 hosts: - myapp.example.com ssl: false
# Multiple hostnames proxy: port: 3000 hosts: - myapp.example.com - www.myapp.example.com ssl: true

Wildcard subdomains - a hosts entry may be a single-label wildcard, so one service can catch every direct subdomain of a domain (foo.example.com and bar.example.com both match; the nested deep.foo.example.com doesn’t, since * only covers one label; neither does the bare example.com):

proxy: port: 3000 hosts: - "*.example.com"

A wildcard host cannot use ssl: true (jiji-proxy’s automatic certificate provisioning can’t issue a wildcard certificate); provide your own certificate via ssl: { certificate_pem, private_key_pem } instead, or skip TLS for that host. See the Proxy Reference for details.

HTTP health check:

proxy: port: 3000 hosts: - myapp.example.com healthcheck: path: /health interval: 10s timeout: 5s deploy_timeout: 60s

Command health check (cmd takes precedence if both cmd and path are set - not enforced as a validation error, so pick one):

proxy: port: 3000 hosts: - myapp.example.com healthcheck: cmd: "test -f /app/ready" # exit 0 = healthy cmd_runtime: docker # optional, defaults to builder.engine interval: 10s timeout: 5s deploy_timeout: 60s

Other command examples: pgrep -f myapp, /app/healthcheck.sh, curl -f http://localhost:3000/health.

Path prefix routing:

proxy: port: 3000 hosts: - myapp.example.com path_prefix: /api

Multiple targets (multiple ports on one service):

proxy: targets: - port: 3900 hosts: - s3.example.com healthcheck: path: /health - port: 3903 hosts: - admin.example.com ssl: true healthcheck: cmd: "test -f /ready"

Raw TCP proxying publishes a non-HTTP service on a dedicated public TCP port. port is the backend container port and listen_port is the public port accepted by jiji-proxy:

services: postgres: image: postgres:18 servers: - db1 - db2 proxy: port: 5432 listen_port: 15432 healthcheck: cmd: "pg_isready -U appuser -d app"

Setting listen_port selects raw TCP mode. It cannot be combined with HTTP-only path_prefix or ssl. Ports 0, 80, and 443 are reserved, and every TCP route on a shared host needs a unique public port. hosts is optional metadata for a TCP target, not a routing key. Open the configured listen_port in the server firewall and point clients at any ingress server that owns the route. See the Proxy Reference for routing and multi-project details.

Container Runtime options

services: worker: image: myapp:latest servers: - server1 command: ["./run-worker.sh", "--queue", "high"] # or as a string: command: "./run-worker.sh --queue high"
services: monitoring: image: prometheus:latest servers: - server1 network_mode: bridge # bridge (default), service:<other-service-name>, or host

network_mode: none is rejected by validation: every service needs a reachable address for DNS and health checks. Use service:<name> to explicitly share another service’s namespace, or crons: for isolated one-off/scheduled work. Docker’s native container:<id> syntax is also rejected outright by validation.

Host networking (network_mode: host) — shares the host’s own network namespace instead of getting a project-bridge address.

services: app: image: nginx:latest servers: - server1 network_mode: host ports: - "8080" # what the service listens on; never rendered as -p

ports: accepts at most one entry, a bare container-side port number only (never a host:container mapping or a /udp suffix): it’s metadata for the per-server uniqueness check below, not a port mapping — the app must already bind the port it wants directly on the host. Two host-mode services whose servers overlap can’t declare the same port; this check only sees services within the same project, so a different project’s host-mode service on a shared machine can still collide, surfacing as a failed container start instead of a validation error. proxy: and scale above 1 are rejected, the same as for service:<name> sharing. Host networking removes jiji’s mesh isolation for that container: it can reach, and be reached by, anything the host’s own network can, bypassing the project bridge entirely.

Container namespace sharing (network_mode: service:<name>) — shares another (“upstream”) service’s container network namespace instead of getting its own dynamically-leased address. This is the standard “VPN killswitch” pattern: a torrent client sharing a VPN gateway container’s network stack, so all its traffic is forced through the tunnel.

services: gluetun: image: qmcgaw/gluetun:latest servers: - server1 proxy: port: 8080 hosts: - torrents.example.com qbittorrent: image: lscr.io/linuxserver/qbittorrent:latest servers: - server1 network_mode: service:gluetun

Naming the upstream via network_mode is itself the dependency declaration — there’s no separate depends_on field. Redeploying gluetun (via jiji deploy, jiji service restart, or jiji service rollback) automatically redeploys qbittorrent too, sequenced strictly after gluetun’s own deploy finishes. A dependent can’t set scale above 1 or configure its own proxy: block: traffic reaches it through the upstream’s own route, at the upstream’s address. The upstream cannot itself be a network_mode: service:<name> dependent because chained sharing is not supported. The dependent’s servers must be a subset of the upstream’s.

services: api: image: myapp:latest servers: - server1 cpus: 2 # or a string: "1.5" memory: "512m" # "512m", "1g", "2gb"
services: ml-worker: image: pytorch:latest servers: - gpu-server gpus: "all" # or "0", "0,1", "device=0"
services: video-processor: image: ffmpeg:latest servers: - server1 devices: - "/dev/video0" - "/dev/snd"
services: system-tool: image: debug-tools:latest servers: - server1 privileged: true # use with caution
services: network-tool: image: nettools:latest servers: - server1 cap_add: - NET_ADMIN - SYS_PTRACE

Stateful services (stop_first)

services: database: image: postgres:15 servers: - db-server stop_first: true

Use stop_first: true for services that can’t run two instances at once - file-based locks (LevelDB, SQLite), or coordinator registration by a unique ID. This trades zero downtime for a brief stop-then-start window during deploy. Default is false.

Restart policy

services: worker: image: myapp/worker:latest servers: - server1 restart: on-failure # unless-stopped (default), always, on-failure, no

Passed straight through to the container engine’s --restart flag.

Image retention (retain)

services: web: build: context: . servers: - server1 retain: 5 # optional, default: 3

Only relevant for build-configured services (a static image: service is never pruned). Sets how many recent image tags each server keeps before removing the rest, as long as they aren’t still referenced by a running container. After every successful deploy, restart, or rollback, jiji pushes this value to every server in the service’s servers: list, and each server’s jiji-agent prunes to it continuously in the background - no need to run anything manually for routine cleanup. Run jiji service prune (or jiji service prune --retain N) to prune immediately instead of waiting for the next reconcile tick, or to override this value for one run.

Project limits and validation

Jiji rejects a configuration that exceeds these project limits:

ResourceMaximum
Servers32
Services500
Logical replicas, across all services2,000
Cron jobs on one service32
Cron jobs across the project1,000

Scaled services must use project bridge networking. A service with more than one replica cannot use local volumes, managed files or directories, privileged mode, devices, or GPUs. A stop_first service must remain a singleton.

For network_mode: service:<name>, the dependent must remain a singleton. Its server list must be a subset of the upstream server list. Chained namespace sharing is not supported.

Raw TCP proxy ports must be unique within one project. Ports 80 and 443 are reserved for HTTP ingress. A raw TCP target cannot set path_prefix or ssl. Jiji-proxy also rejects conflicts with routes from other projects on the same host when it applies the route.

Complete example

project: myapp-production builder: engine: docker cache: false registry: server: ghcr.io username: myorg password: GITHUB_TOKEN ssh: user: deploy keys: - ~/.ssh/production_key proxy: bastion.example.com environment: clear: APP_ENV: production LOG_LEVEL: warn servers: web1: host: web1.example.com web2: host: web2.example.com api1: host: api1.example.com db1: host: db1.example.com services: web: build: context: ./web dockerfile: Dockerfile.production servers: - web1 - web2 ports: - "3000" proxy: port: 3000 hosts: - app.example.com - www.app.example.com ssl: true healthcheck: path: /health interval: 10s api: build: context: ./api servers: - api1 ports: - "4000" environment: secrets: - DB_PASSWORD proxy: port: 4000 hosts: - api.example.com ssl: true path_prefix: /api healthcheck: path: /api/health database: image: postgres:15 servers: - db1 volumes: - "/data/postgres:/var/lib/postgresql/data" environment: clear: POSTGRES_PASSWORD: DB_PASSWORD

Validation

Jiji validates configuration before it runs a command. It collects applicable validation errors after YAML deserialization succeeds. A YAML or type error can stop deserialization before those checks run. Common validation failures include:

Error: A project supports at most 32 servers Error: Registry credentials require builder.registry.server Error: A service with stop_first must remain a singleton

A typo’d field name is not always caught. servers:, builder:, environment:, build:, and the top-level document reject unknown YAML keys silently rather than erroring - a typo like pott: instead of ports: inside a service is just ignored. ssh:, network:, proxy:, individual server entries, and crons: entries do reject unknown keys. When a setting doesn’t seem to take effect, double-check spelling against this reference rather than assuming Jiji would have flagged it.

Run with -v/--verbose for more detailed validation output:

jiji -v deploy
Last updated on