Compose and stack files

Deploying a Docker Compose or Swarm stack file - what is applied, what is not, and what applying it again does.

A Compose file describes several programs at once. ISOGrid reads it and turns each service into an application of its own: the same kind of application you would create by hand, with its own size, address, logs and history. A docker stack file is read the same way.

The file is a way of creating and updating applications. It is not something that keeps running as a group afterwards.

Two kinds of service, two paths

A service that names an image is pulled and run. That is what this page covers.

A service with a build: section is built from its Dockerfile when you import the repository. That path reads the file differently, and how the build is detected describes it.

A file can hold both. Each service takes the path that fits it.

Where the file comes from

Pasted or uploaded. Applications, then From a stack file: paste the YAML or upload it.

From a connected repository. When you pick a repository, the YAML files in it whose services name images are listed as stack files. The platform reads the file itself, shows you what it read and at which commit, and applies that same commit even if someone pushes in between.

From the command line. isogrid stack preview -f compose.yml --stack shop and isogrid stack apply, with a local file, a folder of files, or --repo and --path for a file in a connected repository. This is the one to run from CI.

A file may be up to 256 KB and hold up to 60 services.

Preview first

A preview changes nothing. For each service it says whether an application would be created, updated or skipped and why, which of your registries pulls the image, and everything in the file that will not be applied.

You then decide, per service: the size (the smallest that fits the file's limits is suggested), the network, the registry when several match, the port, where it may run, or to leave it out.

${TAG} and ${TAG:-1.4} are filled from the variables you give. A variable the file uses and nobody supplied is listed, and blocks the apply.

What is applied

  • image, which is required.
  • deploy.replicas, from 1 to 10.
  • environment.
  • command, entrypoint, user, healthcheck and stop_grace_period.
  • deploy.update_config, rollback_config and restart_policy.
  • logging with the json-file driver: max-size and max-file turn log retention on for that application.
  • ports and expose, only to learn which port the program listens on.
  • deploy.resources.limits, only to suggest a size.
  • networks, matched by name to one of your networks. An application joins one network; the first one listed is used.
  • Anchors and <<: merge keys.

What is not, and why

build: without an image. The service is skipped here. Import the repository to build it, or push the image and name it.

Databases, caches, brokers and storage servers. An image of PostgreSQL, MySQL, MongoDB, Redis, RabbitMQ, Kafka, Keycloak, MinIO and the like is skipped, with a pointer to the managed equivalent. Run as an ordinary application it would have no backups and would lose its data on the first move. See Databases and Managed services.

env_file. Not read on this path. Add those variables on the application afterwards, and secrets as secrets.

volumes. Not mounted, named or bind. A service cannot choose host paths on shared machines. Attach storage on the application's page after applying.

secrets and configs. Not created. Store the values as secrets and bind them to the application.

Published host ports. "80:8080" opens nothing on a machine. The application is private until you make it public from its exposure settings, and then it gets an address.

cap_add, privileged, devices, network_mode and similar. Not granted. The preview lists each key it did not apply.

depends_on, container_name, hostname, labels, top-level restart. Ignored without a warning. Nothing is started in order: an application that needs another one should retry until it answers.

Placement constraints. They name someone else's machines. One is kept only when it matches a choice the region offers.

Smaller things. mode: global runs as ordinary copies, resource reservations come from the size, and log drivers other than json-file are not available.

Names, and how services find each other

Each application is called <stack>-<service>. On its private network it also answers to the plain service name from the file, so http://api:8000 keeps working between services on the same network.

An application that already has that name and was not created by this stack is left alone.

An example the platform accepts

services:
  api:
    image: ghcr.io/acme/api:${TAG:-1.4}
    command: ["node", "server.js"]
    environment:
      LOG_LEVEL: info
    ports:
      - "8000"
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:8000/health"]
      interval: 30s
      timeout: 5s
      retries: 3
    deploy:
      replicas: 2
      resources:
        limits:
          cpus: "0.5"
          memory: 512M
      update_config:
        parallelism: 1
        order: start-first
        failure_action: rollback
        monitor: 30s
    logging:
      driver: json-file
      options:
        max-size: 10m
        max-file: "3"
  worker:
    image: ghcr.io/acme/api:${TAG:-1.4}
    command: ["node", "worker.js"]

Applying it again

Applying the same file under the same stack name updates the applications instead of creating new ones.

The file wins for what it sets. Image, copies, command, health check, rollout and log settings are replaced by the file's.

Environment is merged. The file's variables overwrite the same names; variables you added by hand are kept.

The number of copies goes back to the file's. If you scaled an application by hand, the next apply undoes it.

The size is what that apply chooses. From the command line, pass --size every time, or the application returns to the suggested size.

Nothing is deleted. A service removed from the file leaves its application running. Delete it yourself.

Every created or updated application is then deployed, even when nothing changed, unless you turn deployment off. One service failing does not stop the others; each is reported.

Rolling out and reading logs

Each application rolls out on its own, by the settings the file gave it. failure_action: rollback returns to the previous version automatically, pause leaves the decision to you, and continue does neither. monitor is how long a new version must stay healthy, between 5 and 300 seconds.

isogrid stack apply --wait waits for every deployment and fails if any failed or was rolled back, which is what a CI job needs. --follow prints the deployment logs as they come.

Logs are read per application, not per stack: on the application's page, or with isogrid apps logs <application>. Without log retention, only what the running copies still hold can be searched.

What to read next