Using Portainer Git‑Based Stacks for Simple GitOps on Docker Hosts
Learn how Portainer’s Git‑based Stacks let you version‑control Docker Compose files and automate redeploys on Docker hosts, with a concrete example, trade‑offs, and verification steps.
22 Aug 2026, 11:34 UTC

The problem: keeping Docker Compose deployments in sync with source code
When you run a multi‑container app on a single Docker host, it’s tempting to paste a docker‑compose.yml into Portainer’s Stack editor and click “Deploy”. That works until you need to change the image tag, add a volume, or tweak an environment variable. Each edit means opening the UI, copying the new YAML, and hoping you didn’t introduce a typo. Worse, if someone later runs docker compose up -d manually on the host, the stack in Portainer drifts away from the version you think is deployed.
Thesis: Portainer’s Git‑based Stacks give you a lightweight GitOps loop without leaving the Docker host
Portainer can treat a Git repository as the source of truth for a Stack. When you point a Stack at a repo URL, branch, and the path to a docker‑compose.yml, Portainer polls the repository (or reacts to a webhook) and redeploys the stack whenever the file changes. This gives you version‑controlled definitions, easy rollback via Git history, and a clear audit trail—while still running on plain Docker or Swarm.
How it works (conceptual flow)
- Git repository: holds the Compose file (and optionally a
.envfile referenced by the stack). - Portainer server: reads the repo URL, branch, and file path; stores credentials securely; schedules polling or listens for webhooks.
- Docker host / Swarm: receives the redeploy request and brings up the services defined in the latest Compose file.
Note: Portainer does not> continuously reconcile manual changes made with the docker CLI on the host; it only reacts to detected Git changes.
Worked example: deploying an NGINX service from Git
- Create a test repository (any Git host).
mkdir portainer-nginx-demo && cd portainer-nginx-demo git init echo 'version: "3.8"\nservices:\n web:\n image: nginx:stable-alpine\n ports:\n - "8080:80"' > docker-compose.yml git add docker-compose.yml git commit -m "Initial compose" git branch -M main # Push to your remote (replace with your repo) git remote add origin git push -u origin main - Add the stack in Portainer (UI steps are generic; exact labels vary by edition).
- Navigate to Stacks → Add stack.
- Choose Repository as the source.
- Fill in:
- Repository URL:
- Branch:
main - Compose file path:
docker-compose.yml(relative to repo root) - If the repo is private, add a username/personal‑access‑token under Repository authentication.
- Toggle Automatic updates (or set a polling interval, e.g., 60 seconds).
- Repository URL:
- Click Deploy the stack. Portainer clones the repo, reads the Compose file, and starts the NGINX container on port 8080.
- Verify the initial deployment.
Run on the host:curl http://localhost:8080– you should see the NGINX welcome page. - Trigger an update via Git.
Edit the Compose file to change the exposed port, commit, and push:sed -i 's/8080/8081/' docker-compose.yml git commit -am "Change host port to 8081" git pushWait for the polling interval (or push a webhook if configured). Portainer should redeploy the stack; the NGINX container will be recreated and now listen on port 8081. Verify:curl http://localhost:8081returns the welcome page, while the old port no longer responds. - Rollback using Git history.
Revert the last commit:git revert HEAD --no-editand push. Portainer detects the revert and redeploys the original Compose file, moving the service back to port 8080.
Trade‑offs and limitations
- Pull‑only model: Portainer only redeploys when it detects a change in the Git repository. Manual changes made with
docker composeor the Docker CLI on the host are not reverted; they cause drift that Portainer will not notice until the next Git‑triggered redeploy. - Secret handling: Compose files stored in Git should avoid plaintext passwords. Use environment variables that reference Docker secrets, external vaults, or Portainer’s Stack → Environment variables feature.
- Edition differences: Webhook support, polling interval configurability, and the exact location of the “Automatic updates” toggle vary between Portainer CE and Business Edition and across versions. Consult your instance’s UI or documentation to confirm the available options.
Actionable checklist
- Create a Git repo with a
docker‑compose.yml(keep secrets out of the file). - In Portainer, add a new Stack, select Repository as the source, enter repo URL, branch, and file path.
- If the repo is private, add credentials under Repository authentication.
- Enable Automatic updates (or set a polling interval that suits your change frequency).
- Deploy the stack and verify the containers are running.
- Make a visible change to the Compose file, commit, push, and confirm that Portainer redeploys (check logs or service ports).
- Test rollback by reverting a commit and observing the stack return to the previous state.
By treating your Compose file as a version‑controlled artifact, you gain auditability, reproducible deployments, and a simple way to revert bad changes—all without leaving the familiar Portainer UI or provisioning a full Kubernetes GitOps controller.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.