Git as the Source of Truth: A Practical Guide to GitOps with Portainer

Git as the Source of Truth: A Practical Guide to GitOps with Portainer
Photo by Yancy Min / Unsplash

Introduction

GitOps is transforming how modern teams manage infrastructure and application deployments. At its core, GitOps treats a Git repository as the "single source of truth" for stack definitions, configuration, and environment variables. This approach brings transparency, versioning, and automation to container orchestration—especially when paired with tools like Portainer.

This guide details:

  • Why and how to define your stacks in Git
  • Setting up a GitOps workflow in Portainer
  • Automating deployments with polling and webhooks
  • Separating CI from CD for robust, auditable workflows

The Core Concept: Git as the Single Source of Truth

Instead of making manual changes in the Portainer UI or running docker-compose up on your server, all stack definitions and configuration are stored in a Git repository. This includes:

  • docker-compose.yml (or Kubernetes manifests)
  • Service configuration files (e.g., nginx.conf, app settings)
  • Environment variable files (e.g., .env)

Key benefits:

  • Declarative infrastructure: The desired state is defined in code.
  • Version control & auditability: Every change is tracked, reviewed, and can be rolled back.
  • Self-service and collaboration: Changes are proposed via pull requests, enabling peer review and collaborative workflows.
  • Automated reconciliation: Portainer ensures the actual running state matches the Git repository.

Workflow Example:

  • To update a service (e.g., change an image tag, modify a port), edit the relevant file in Git, commit, and push.
  • Portainer automatically detects the change, pulls the latest repo version, and redeploys the stack.

Setting Up the GitOps Workflow in Portainer

Portainer supports GitOps out-of-the-box, allowing stack deployments directly from a Git repository. Follow these steps to set it up:

1. Prepare Your Git Repository

  • Structure your repository to include:
    • docker-compose.yml or Kubernetes manifests
    • Any necessary configuration files and directories
    • Environment variable files if required

Example repository layout:

/my-stack-repo
  ├── docker-compose.yml
  ├── nginx/
  │   └── nginx.conf
  ├── .env
  └── app/
      └── settings.yaml

2. Create/Configure a Stack in Portainer

  • Log in to the Portainer UI.
  • Navigate to Stacks and click + Add stack.
  • Set a stack name.
  • For Build method, select Git repository.

3. Configure the Git Repository

  • Repository URL: Enter the HTTPS or SSH URL for your repo.
  • Compose Path: Specify the relative path to your docker-compose.yml or manifest file (e.g., nginx/docker-compose.yml).

Authentication:

  • For private repositories, provide credentials:
    • GitHub: Use a Personal Access Token (PAT) with appropriate repo access. Store this securely and provide it in Portainer.
    • SSH: Optionally, use SSH keys for repo access.

Additional Options:

  • Environment variables: If your compose file references environment variables, define them explicitly in Portainer or ensure they exist in the repo.
  • Local filesystem paths: If services use additional directories or configs, ensure these are present and correctly referenced in the repo structure.

4. Deploy the Stack

  • Click Deploy the stack.
  • Portainer clones the repository, reads the compose file, and starts the services.
  • Any subsequent changes pushed to the specified branch can be automatically synchronized.

Automating Deployments: Polling vs. Webhooks

Portainer provides two main methods to automate stack redeployment when your Git repository changes:

Polling (Pull Method)

  • Portainer checks the Git repository at a regular interval (e.g., every 5 minutes).
  • If a new commit is found on the tracked branch, Portainer pulls the latest version and redeploys the stack.
  • Use case: Suitable for most environments where a short delay is acceptable and immediate deployment is not critical.

Webhooks (Push Method)

  • Portainer provides a unique webhook URL for your stack.
  • Configure your Git provider (e.g., GitHub, GitLab, Gitea) to send a webhook to this URL on repository push or merge events.
  • On webhook receipt, Portainer immediately pulls the latest code and redeploys the stack.
  • Use case: Enables instant deployments, ideal for production CI/CD pipelines where changes need to go live immediately after being merged.

Comparison Table:

Method Trigger Mechanism Latency Use Case
Polling Periodic Git check Minutes Non-urgent, routine updates
Webhooks Push event trigger Seconds Instant, production-grade deployments

How GitOps with Portainer Separates CI from CD

The Portainer GitOps workflow is focused on Continuous Deployment (CD). Continuous Integration (CI)—building, testing, and publishing Docker images—is handled by separate tools (such as GitHub Actions, GitLab CI, Jenkins).

Typical CI/CD Workflow:

  1. Code Change
    • Developer pushes code to application's Git repository.
  2. CI Pipeline (e.g., GitHub Actions)
    • Pipeline builds a new Docker image from the updated code.
    • Automated tests run.
    • On success, the image is pushed to a container registry (e.g., Docker Hub, GitHub Container Registry) with a new tag.
  3. Deployment Trigger
    • The CI pipeline commits an update (e.g., bumps the image: tag) to a deployment repository containing your docker-compose.yml.
    • This triggers Portainer's GitOps workflow (via polling or webhook).
  4. CD by Portainer
    • Portainer detects the new commit.
    • It pulls the updated compose file.
    • It redeploys the stack using the new image from the registry.

Why this Separation Matters:

  • Best practice: Keeps application code and deployment configuration in distinct repositories.
  • Security & auditability: Limits the scope of what each system can change.
  • Flexibility: CI pipelines can be as complex as needed, while CD remains simple and declarative.

Key Advantages of GitOps with Portainer

  • Transparency: All stack changes are tracked in Git, enabling full audit trails and easy rollbacks.
  • Reproducibility: Anyone can reproduce the current production environment by cloning the repository and applying the stack.
  • Collaboration: Changes are reviewed and approved through standard Git workflows (pull requests, code review).
  • Automation: Portainer ensures the deployed state always matches the desired state described in Git, reducing manual intervention.
  • Separation of Concerns: CI and CD responsibilities are cleanly delineated, improving security and maintainability.