This guide walks you through deploying a Docker Compose stack to Portainer using Terraform.
If you are new to Terraform, the core idea is straightforward: you describe what you want in configuration files, and Terraform figures out how to make it happen. You declare the end state, and Terraform does the work.
Prerequisites
To get started, you’ll need:
• A running Portainer instance with admin access
• At least one Docker environment already connected to Portainer
• Terraform v1.0 or later installed on your local machine
1. Set up your project
Note: The Portainer Terraform provider supports authentication using either an API key or username and password. This example uses API key authentication, which is recommended for automation.
Create a new directory for your Terraform configuration and navigate into it.
mkdir portainer-stack
cd portainer-stack
Create a main.tf file and add the provider configuration below. This tells Terraform where your Portainer instance is and how to authenticate with it.
# main.tf
terraform {
required_providers {
portainer = {
source = "portainer/portainer"
}
}
}
provider "portainer" {
endpoint = var.portainer_url
api_key = var.portainer_api_key
}
Create a variables.tf file to declare the variables used in your configuration:
# variables.tf
variable "portainer_url" {
description = "The URL of your Portainer instance"
type = string
}
variable "portainer_api_key" {
description = "Portainer API key for authentication"
type = string
sensitive = true
}
Create a terraform.tfvars file to supply your Terraform configuration variables.
Important: Never commit credentials directly to source control, consider using a secrets manager.
# terraform.tfvars
portainer_url = "https://portainer.example.com"
portainer_api_key = "your-api-key-here"
Now initialise Terraform. This downloads the Portainer provider plugin and prepares your working directory:
terraform init
You should see output confirming the provider has been installed successfully.
2. Look up your environment
Note: The name value must exactly match the name of an existing environment in your Portainer instance. You can find this under Environments in the Portainer UI.
Create a file called data.tf containing your environment name:
# data.tf
data "portainer_environment" "target" {
name = "my-docker-env"
}
When Terraform runs, it will query your Portainer instance for an environment matching that name and make its ID available to other resources as data.portainer_environment.target.id. You will use this in the next step.
3. Define your stack
Create a file called stack.tf. This is where you define the stack you want Portainer to deploy. In this example we are deploying a simple nginx web server, which is a good starting point for getting familiar with the workflow.
The deployment_type is set to "standalone" to tell Portainer this is a regular Docker Compose deployment, as opposed to Docker Swarm or Kubernetes.
The method is set to "string", which means the manifest content is provided inline, directly in the Terraform configuration.
The endpoint_id references the environment ID found in the previous step using the data source.
stack_file_content uses Terraform’s heredoc syntax (<<-EOT … EOT) to write a multiline string inline. The content is a standard Docker Compose file.
# stack.tf
resource "portainer_stack" "first_stack" {
name = "my-first-stack"
deployment_type = "standalone"
method = "string"
endpoint_id = data.portainer_environment.target.id
stack_file_content = <<-EOT
version: "3.8"
services:
web:
image: nginx:latest
ports:
- "8080:80"
restart: unless-stopped
EOT
}
4. Preview the changes with terraform plan
Before making any changes to Portainer, Terraform lets you preview exactly what it intends to do. Run:
terraform plan -out stack.tfplan
Terraform will connect to your Portainer instance, compare the current state to what your configuration describes, and print a summary of what it plans to do.
Each line prefixed with + means that resource will be created.
Lines with ~ indicate updates
Lines with - indicate deletions.
The -out flag saves the plan to a file. This guarantees that when you run apply, Terraform executes exactly what you reviewed and nothing more. If you skip it, Terraform will generate a fresh plan at apply time and ask you to confirm interactively.
5. Deploy the stack with terraform apply
Once you are happy with the plan, apply it:
terraform apply stack.tfplan
Terraform will carry out the actions shown in the plan and report back as each one completes.
Once finished, you will see a summary confirming how many resources were added, changed, or destroyed.
Terraform also writes a state file terraform.tfstate to your project directory. This records what has been created and is how Terraform knows what already exists the next time you run plan or apply. To update your stack later, edit stack.tf and run plan and apply again. Terraform will update the stack in place rather than recreating it from scratch.
Important: The state file can contain sensitive values - do not commit it to source control.
6. Verify the stack is running and optionally remove the stack
Navigate to your environment and select Stacks in the Portainer UI. You should see your stack listed. If you want to remove the stack from Portainer, run terraform destroy. Terraform will show you what will be deleted and ask you to confirm before proceeding.
terraform destroy
Important: Terraform destroy removes everything Terraform manages in your current configuration. Only run this when you intend to fully remove the resources.
Further examples
An extensive list of additional examples - including edge stacks, registries, S3 backups, and more - can be found in the Portainer Terraform provider GitHub repository.
Full provider documentation is available within the Terraform docs.
Try Portainer with 3 Nodes Free
If you're ready to get started with Portainer Business, 3 nodes free is a great place to begin. If you'd prefer to get in touch with us, we'd love to hear from you!

