Skip to content

Local Docker tutorial

This tutorial deploys a real HTTP service without cloud credentials. It uses the repository's Docker demo to build an OCI image, pass its immutable digest to Terraform, and run the resulting container locally.

Run from a source checkout

You need Docker and mise. The demo creates all mutable state below .docker-demo-state/ and never writes deployment refs to the gitopsctr repository or its remote.

Inspect the authored project

The demo source is under demo/docker/repository/:

  • gitopsctr.yaml declares the Project and its environment directory.
  • deployment/environments/dev/environment.yaml declares the dev environment.
  • deployment/stack-templates/application.yaml declares reusable image and deployment Unit templates.
  • deployment/environments/dev/stacks/application.yaml instantiates them as one Stack in the application apply partition.

The service obtains the immutable image URI from the image unit:

image:
  fromArtifact:
    unit: image
    name: containers
    apiVersion: artifact.gitopsctr.io/v1
    kind: ContainerImages
    pointer: /images/application/uri
flowchart LR
  image["application/image<br/>OciImages"] -->|publishes| artifact["application/image/containers<br/>ContainerImages"]
  artifact -->|fromArtifact| service["application/deploy<br/>Terraform"]

This template-local reference creates a dependency: the image Unit must publish its receipt and containers artifact before deploy can be resolved. At the CLI boundary those Stack-owned Units are addressed as application/image and application/deploy.

Deploy

Install the development tools and start from an empty demo state:

mise install
mise run sync
mise run demo-docker run

The runner creates an isolated Git repository, starts a local registry, then runs converge with the Stack file and partition as explicit inputs. Convergence applies the Stack to gitopsctr/desired/dev, reconciles application/image, publishes its receipt and artifact to gitopsctr/observed/dev, reapplies the same input to resolve the image URI, and finally reconciles application/deploy.

The command finishes with the application URL and response. Verify it directly:

curl http://127.0.0.1:18080

Inspect desired and observed state

Change into the demo's isolated repository and start with the namespace overview. The excerpt below omits the Stack and StackTemplate sections, but the important relationship is already visible: both Units are current and the image Unit's Artifact is authenticated.

$ cd .docker-demo-state/repository
$ uv run gitopsctr get all --environment dev
UNITS
NAME                 KIND       PARTITION    DESIRED       OBSERVATION  RECONCILIATION  REASON
application/deploy  Terraform  application  <blob>        CURRENT      CLEAN           observation matches desired state
application/image   OciImages  application  <blob>        CURRENT      CLEAN           observation matches desired state

...

ARTIFACTS
NAME                           KIND             PARTITION    AUTHENTICATION
application/image/containers  ContainerImages  application  CURRENT

The source commit contains authored resources. gitopsctr/desired/dev contains resolved desired Units, Stacks, and StackTemplates; gitopsctr/observed/dev contains Receipts and Artifacts proving what was applied. The default Unit table combines those planes into an operational summary.

Use the copyable hierarchical address from the Artifact table for an exact lookup. --as-list keeps the exact Artifact document inside the generic inspection envelope so that its address, Git provenance, and derived authentication state remain available:

$ uv run gitopsctr get artifact application/image/containers --environment dev -o yaml --as-list
apiVersion: inspection.gitopsctr.io/v1
kind: ResourceList
metadata: {}
items:
- provenance:
    environment: dev
    plane: observed
    ref: gitopsctr/observed/dev
    revision: <observed-commit>
    path: artifacts/application/image/containers.yaml
  address:
    family: artifact
    scope: environment
    namespace: dev
    qualifiedName: application/image/containers
  document:
    apiVersion: artifact.gitopsctr.io/v1
    kind: ContainerImages
    metadata:
      name: containers
    # producer and images omitted here
  inspection:
    authentication: CURRENT

Without --as-list, that named query returns only the persisted ContainerImages document. A collection query such as get artifacts always returns a ResourceList for YAML/JSON output, even when it contains one item. See Concepts for the full state model and Operations for ref/revision overrides.

The same registry rule drives addressing and storage. The Unit document keeps the local metadata.name: image while its Stack placement stores it at units/application/image.yaml; its Receipt mirrors that path and its Artifacts are nested below artifacts/application/image/.

Partitions and independent resources

This tutorial applies one authoritative partition. If the Stack is removed from the next application of that partition, gitopsctr begins deletion of the Stack and its generated Units:

gitopsctr apply --environment dev \
  --partition application \
  --file deployment/stack-templates/application.yaml \
  --source-revision HEAD

Applying without --partition updates only explicitly supplied roots. Existing resources keep their partition; new resources remain unpartitioned. Run converge --environment dev --yes after recording deletion intent to let the controller tear down generated Units child-first and remove the Stack when safe. See Preview environments for the unpartitioned preview workflow and automatic deletion progression.

Prove clean convergence

Return to the source checkout and run the demo again:

cd ../..
mise run demo-docker run

With unchanged source and external state, no driver runs and neither deployment ref moves. This idempotent second run is the steady state that converge aims for.

Remove the containers, images, registry, isolated Git repository, receipts, and Terraform state when finished:

mise run demo-docker clean

The demo README is the concise operational reference for reruns, acceptance checks, port overrides, and cleanup.