Portainer Stacks: Declarative Compose Deployments and the Manual-Change Trap
Portainer Stacks treat Docker Compose as the source of truth. Learn when to use them, how manual edits get overwritten, and how to verify a redeploy.
03 Jul 2026, 14:45 UTC

The Problem: Manual Container Edits That Disappear
You open Portainer, find a container running low on memory, and raise its limit in the UI. The application stabilizes. A week later, a teammate redeploys the stack, and the container crashes again with the same out-of-memory error. The manual change is gone.
This happens because Portainer Stacks treat a Docker Compose file as the desired state. Anything you change directly on a container—environment variables, resource limits, port bindings—exists only until the next stack update. Portainer then recreates the containers to match the Compose specification, discarding ad-hoc tweaks.
The useful takeaway: if your application needs more than one container, manage it as a Stack rather than as individual containers. Decide whether the Compose file lives in the Portainer web editor or in a Git repository, and treat manual UI changes as temporary debugging only.
How a Portainer Stack Works
A Stack is Portainer's wrapper around Docker Compose. When you deploy a Stack, Portainer sends the Compose file to the Docker engine on the selected endpoint. The engine creates the networks and volumes defined in the file, starts the services, and uses service names as DNS entries so containers can reach each other without hardcoded IP addresses.
Portainer also provides a UI for environment variables. You can add, edit, or remove variables without touching the YAML, then redeploy. The variables are injected at container startup, which is safer than storing a .env file on the host.
Version assumption: this behavior applies to Portainer Community Edition and Business Edition using the Docker Compose stack engine. Write Compose files compatible with the Docker engine running on your endpoint; unsupported keys will cause deployment errors.
Worked Example: Deploy a Two-Service Stack
This example deploys a small Nginx frontend and a Redis cache using the Portainer web editor.
- Where to run: In the Portainer web UI, under Stacks > Add stack. Required permission: Stack management access on the target endpoint, typically an administrator or a user with stack write rights.
- Configuration: Choose Web editor, name the stack
example-app, and paste the following Compose file:
services:
web:
image: nginx:alpine
ports:
- "8080:80"
environment:
- REDIS_HOST=cache
depends_on:
- cache
cache:
image: redis:alpine
- Deploy: Click Deploy the stack. Portainer creates a network so
webcan resolvecacheby service name. - Expected check: Open the stack's resource list. Both containers should show Running. From a host with network access, run
curl -I http://<endpoint-ip>:8080and expect an HTTP response from Nginx. - Risk: If port 8080 is blocked by the host firewall or cloud security group, the container can be healthy while the browser receives a timeout. Verify inbound access before assuming the stack failed.
Git Sync and the Overwrite Trade-off
For repeatable deployments, connect the Stack to a Git repository instead of pasting YAML into the editor. Portainer pulls the Compose file from a branch and can redeploy on a schedule or via webhook. This makes Git the single source of truth.
The trade-off is operational discipline. Because Portainer reconciles the running containers with the Compose file on every update, any manual change made through the container menu is overwritten. If you troubleshoot by editing a live container, document the change and add it to the Compose file, or it will vanish on the next redeploy.
Git synchronization also requires network line-of-sight. The Portainer server must reach the Git provider over HTTPS (port 443). A private repository behind a corporate firewall needs a proxy or outbound rule, otherwise the pull and redeploy step will fail silently or with an authentication error.
Verify the Result After a Change
To confirm the stack is declarative, change an environment variable in the Stack settings—such as updating REDIS_HOST—and click Update the stack. Portainer recreates the affected containers. Inspect the container details or logs to verify the new value is present. If the old value remains, the redeploy did not propagate, which usually indicates a syntax error in the Compose file or a failed pull from Git.
Rollback for a state-changing update: redeploy the previous version of the Compose file from Git history or restore the earlier YAML in the web editor, then update the stack again.
Closing
Use Portainer Stacks when you want a single, auditable definition for a multi-container application. Avoid treating individual containers as long-lived configuration targets. For teams, prefer Git-backed stacks so changes are reviewed before they reach the endpoint. The practical check is simple: after every redeploy, confirm that the running configuration matches the Compose file, not the last manual UI tweak.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.