Programmatic Docker Compose Stack Deployment in Portainer: Agentless vs Agent-Based Endpoints
Deploy Docker Compose stacks via Portainers API, choose between agentless and agent-based endpoints, and verify deployment health in heterogeneous container environments.
07 Apr 2026, 03:13 UTC

Problem: Deploying Compose Stacks Across Diverse Docker Environments
DevOps teams often manage containers across multiple Docker environments—local laptops, remote servers, Swarm clusters—and need a consistent way to deploy Docker Compose stacks without constantly switching between CLI tools and UIs. Portainer provides a unified interface, but the underlying endpoint mode significantly affects what you can automate and how you verify it.
Endpoint Modes and What They Mean for Compose Deployment
Portainer distinguishes between two endpoint connection modes. Understanding the difference helps you avoid deployment surprises.
Agentless Endpoints
An agentless endpoint connects directly to the Docker daemon API. No extra process runs on the target host. This mode is straightforward to set up: provide the daemon’s socket or TLS endpoint, and Portainer can list containers, images, and volumes. For Docker Compose stack deployment, agentless mode sends the compose definition directly to the daemon, which resolves services, networks, and volumes using its built-in Compose implementation. This works well for simple stacks on standalone Docker engines.
Agent-Based Endpoints
An agent-based endpoint requires a Portainer agent installed on the target host. The agent forwards API calls to the local Docker environment, enabling features that direct daemon access cannot, such as Swarm mode orchestration, secret management, and Kubernetes integration. The agent also provides status reporting and health checks. However, the agent and server versions must stay aligned; a version mismatch can cause deployment failures or missing orchestration capabilities, so regular version checks are part of routine maintenance.
Programmatic Stack Deployment via the Portainer API
For teams integrating Portainer into CI/CD pipelines or automation scripts, the REST API offers a direct path to deploy stacks. The endpoint POST /api/endpoints/{endpoint_id}/docker/stacks accepts a Docker Compose YAML payload and an authentication token. The {endpoint_id} is the numeric identifier shown in the Portainer UI for the target endpoint. Required permissions include Deploy stacks or Manage endpoints for the associated user role. The compose payload follows the standard Docker Compose format, but keep in mind that agentless endpoints use the daemon’s native Compose support, while agent-based endpoints may leverage additional agent features such as network mapping or volume driver coordination.
POST /api/endpoints/123/docker/stacks HTTP/1.1
Host: portainer.example.com
Authorization: Bearer
Content-Type: application/json
{
"Name": "my-web-app",
"Labels": {"environment": "production"},
"Spec": {
"ComposeFile": "docker-compose.yml"
}
}Where to run this: from any machine that can reach the Portainer API endpoint, typically using curl, httpie, or a custom script. A successful response returns HTTP 200 or 201 with a JSON body containing a stack ID; this confirms the deployment request was accepted. If the compose file contains errors or the endpoint is misconfigured, the API returns a 4xx or 5xx status with error details. Risks include invalid YAML syntax, unreachable Docker daemon, expired or insufficient auth tokens, and—when using agent-based endpoints—agent version incompatibility.
Verification, Limitations, and Practical Checks
After issuing a deployment request, confirming that the stack actually arrived matters. Portainer provides a health-check endpoint: GET /api/endpoints/{endpoint_id}/status. For agentless endpoints, a healthy response includes the field operational. For agent-based endpoints, the response includes the agent’s version, status, and a list of managed resources. A practical verification workflow looks like this:
- Run the stack deployment POST and capture the returned stack ID.
- Query the status endpoint with the same endpoint ID.
- Check the response: operational for agentless, or agent online and version-matched for agent-based.
- In the Portainer UI, navigate to the endpoint and verify the new stack appears under Stacks with the expected services running.
Limitations to keep in mind: the free edition of Portainer lacks advanced RBAC, audit logging, and single sign-on, which are available only in Business+ or Enterprise licenses. If your organization requires a trail of who deployed which stack and when, the free edition wont provide that out of the box. Additionally, agent-based endpoints require a Portainer agent; version mismatches between agent and server can cause deployment failures, so include agent version alignment in your maintenance schedule.
Choosing the Right Mode: A Quick Decision Guide
| Feature | Agentless | Agent-Based |
|---|---|---|
| Standalone Docker daemon | Full support | Requires agent, may be overkill |
| Swarm or Kubernetes orchestration | Limited or none | Full support via agent |
| Secret and config management | Basic daemon support | Agent-mediated access |
| Version alignment needed | Not required | Agent and server versions must match |
| Free-edition feature set | Included | Included, but advanced RBAC absent |
Match the endpoint mode to your environment's orchestration needs and your tolerance for agent maintenance. If you are deploying simple Compose stacks on standalone Docker engines, agentless offers the lowest overhead. If you need Swarm features, Kubernetes integration, or finer-grained resource isolation, the agent-based path is the way forward—just budget time for agent version checks.
Getting Started: A Verified Workflow
- Identify the endpoint ID in the Portainer UI; note whether it is agentless or agent-based.
- Prepare a Docker Compose YAML file with your service definitions.
- Run the API POST request, replacing endpoint_id and TOKEN with your values:
curl -X POST "http://portainer.example.com/api/endpoints/{endpoint_id}/docker/stacks" -H "Authorization: Bearer " -H "Content-Type: application/json" -d '{"Name":"my-app","Spec":{"ComposeFile":"docker-compose.yml"}}'- Inspect the HTTP response: a 200/201 with a stack ID means the request was accepted.
- Follow up with
curl -X GET "http://portainer.example.com/api/endpoints/{endpoint_id}/status" -H "Authorization: Bearer "and confirm operational (agentless) or agent status (agent-based). - Check the Portainer UI to verify the stack is listed and its services are running as expected.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.