Skip to content
Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

GitHub Tag MajorGitHub Tag MinorGitHub Release VersionGitHub Dist SizeAction Run UsingWorkflow ReleaseWorkflow TestWorkflow LintQuality Gate StatusGitHub Last CommitCodeberg Last CommitGitHub Repo SizeGitHub Top LanguageGitHub ContributorsGitHub IssuesGitHub DiscussionsGitHub ForksGitHub Repo StarsGitHub Org StarsDiscordKo-fi

Portainer Stack Deploy Action

Portainer Stack Deploy

Deploy, Update or Create a Portainer Stack from a Repository or Compose File. Supports both Swarm and Standalone Docker deployments for Portainer Community and Business Enterprise Edition. Includes most features including file or repo deploy, deploy from other repo, custom headers, and much more...

This action is written from the ground up in Vanilla JavaScript and is not a fork/clone of existing actions. You can view an Action Comparison of all available actions on the website.

View Documentation

- name: 'Portainer Deploy'uses: cssnr/portainer-stack-deploy-action@v1with:
name: stack-namefile: docker-compose.yamlurl: ${{ secrets.PORTAINER_URL }}token: ${{ secrets.PORTAINER_TOKEN }}

Make sure to review the Inputs and checkout additional Examples.

This is a fairly simple action, for more details see src/index.js and src/portainer.js.

No Portainer? You can deploy directly to Docker Swarm or Compose over SSH with: cssnr/stack-deploy-action or cssnr/docker-context-action.

Features

  • Deploy or re-deploy an existing stack otherwise create a new stack.
  • Deploy from a repository or a compose file, see type.
  • Deploy from a different repo than the current one.
  • Provide environment variables in JSON/YAML or file format.
  • Automatically parse Endpoint ID if only one endpoint.
  • Supports Docker Swarm and Docker Standalone.
  • Supports custom headers for services like Cloudflare Zero Trust.
  • To view all features see the Inputs Documentation.

You can get started here or view workflow examples.

Inputs

Important

Visit the Documentation Site for comprehensive, up-to-date documentation.

InputDefault ValueDescription of the Input
nameRequiredStack Name ⤵️
urlRequiredPortainer URL ⤵️
tokenRequiredPortainer Token ⤵️
filedocker-compose.yamlCompose File ⤵️
endpointendpoints[0].IdPortainer Endpoint ⤵️
refcurrent referenceRepository Ref ⤵️
repocurrent repositoryRepository URL ⤵️
tlsskipfalseSkip Repo TLS Verify
prunetruePrune Services
pulltruePull Images
typerepoType [repo, file] ⤵️
standalonefalseDeploy Standalone Stack
env_data-Env JSON/YAML Data ⤵️
env_jsonDEPRECATEDThis has changed to env_data
env_file-Dotenv File Path ⤵️
merge_envfalseMerge Env Vars ⤵️
username-Repository Username ⤵️
password-Repository Password ⤵️
fs_path-Relative Path (BE) ⤵️
headers-Custom Headers JSON/YAML ⤵️
summarytrueAdd Summary to Job ⤵️

For more details, see the Inputs Documentation and Portainer API Documentation.

name

Swarm sack name or Compose project name.

Example: cool-stack

url

Portainer URL.

This is the base url to your Portainer instance.

Example: https://portainer.example.com:9443

token

Portainer API token.

For Instructions to create an API token visit: https://docs.portainer.io/api/access

file

The Docker compose file. This path is relative to your working directory.

If you check out your repository to the root, and the compose file is called docker-compose.yaml, and is in the app directory, set file to: app/docker-compose.yaml

Default: docker-compose.yaml

endpoint

If endpoint is not provided the first endpoint returned by the API will be used. If you only have one endpoint, this will work as expected, otherwise, you should provide an endpoint.

Example: 1

Default: ${endpoints[0]}

ref

This defaults to the reference that triggered the workflow.

If deploying from a different repository than the current one, you may want to specify the ref of that repository to deploy from.

Example: refs/heads/master

Default: ${{ github.ref }}

repo

This defaults to the repository running the action.

If you want to deploy a different repository, put the full http URL to that repository.

Example: https://github.com/cssnr/portainer-stack-deploy-action

Default: ${{ github.server_url }}/${{ github.repository }}

tlsskip

Skips SSL verification when cloning the Git repository. Set to true to enable.

Default: false

prune

Prune services that are no longer referenced (only available for Swarm stacks). Set to false to disable.

Default: true

pull

Pull latest image before deploy. Set to false to disable.

Default: true

type

Type of Deployment. Supports either repo or file.

Default: repo

standalone

Deploy a compose stack instead of swarm. Set to true to enable.

Default: false

env_data

Optional environment variables used when creating the stack.

These can be provided in JSON or YAML format and can be used with env_file. Values in env_file take precedence over these values.

👀 View Example JSON/YAML Data Format

These examples are identical, just different ways of passing the input.

data: | { "key1": "value1", "key2": "value2" }
data: | key1: value1 key2: value2

Warning

Inputs are NOT secure unless using secrets or secure output. Using env_data on a public repository will otherwise expose this data. To securely pass an environment use the env_file option.

env_file

Environment File in dotenv format, parsed using dotenv.

This can be used with env_data. Values in this file take precedence over env_data.

👀 View Environment File Example
- uses: cssnr/portainer-stack-deploy-action@v1with:
env_file: .env
KEY="Value"KEY_2="Value 2"

Note: Additional inputs are excluded for brevity.

merge_env

Set this to true to merge the current environment variables from the existing stack with any newly provided variables in the env_data or env_file inputs.

When not providing the env_data or env_file inputs the current environment variables from the existing stack are always used.

When deploying a new stack, there are no current environment variables to merge, and this has no effect.

Default: false

username/password

Username for private repository authentication when type is set to repo.

This is NOT your Portainer username, see token for Portainer authentication.

fs_path

Relative Path Support for Portainer BE. Set this to enable relative path volumes support for volume mappings in your compose file.

For more info see the Portainer Documentation - Relative Path Support.

headers

Custom Headers in JSON or YAML format for services like Cloudflare Zero Trust.

The headers are parsed with JSON.parse or yaml.load and passed directly to axios.

👀 View Custom Headers Example

YAML

- uses: cssnr/portainer-stack-deploy-action@v1with:
env_data: | CF-Access-Client-Id: ${{ secrets.CF_CLIENT_ID }} CF-Access-Client-Secret: ${{ secrets.CF_CLIENT_SECRET }}

Multi-Line JSON

- uses: cssnr/portainer-stack-deploy-action@v1with:
env_data: | { "CF-Access-Client-Id": "${{ secrets.CF_CLIENT_ID }}", "CF-Access-Client-Secret": "${{ secrets.CF_CLIENT_SECRET }}" }

toJSON Output

- uses: cssnr/portainer-stack-deploy-action@v1with:
env_data: ${{ toJSON(steps.import-secrets.outputs) }}

summary

Write a Summary for the job. To disable this set to false.

To view a workflow run, click on a recent Test job (requires login).

👀 View Example Job Summary

🎉 Created New Stack 112: test_portainer-stack-deploy

Stack Details
ItemValue
ID112
Nametest_portainer-stack-deploy
Filedocker-compose.yml
TypeSwarm
StatusActive
Created2/28/2025, 3:09:16 AM
Updated-
Path/data/compose/112
EndpointID1
SwarmIDwr8i8agdr05n6wsf1tkcnhwik

View Documentation

- name: 'Portainer Deploy'uses: cssnr/portainer-stack-deploy-action@v1with:
token: ${{ secrets.PORTAINER_TOKEN }}url: https://portainer.example.com:9443name: stack-namefile: docker-compose.yaml

Outputs

OutputOutput Description
stackIDResulting Stack ID
swarmIDResulting Swarm ID
endpointIDEndpoint ID
- name: 'Portainer Deploy'id: stackuses: cssnr/portainer-stack-deploy-action@v1with:
token: ${{ secrets.PORTAINER_TOKEN }}url: https://portainer.example.com:9443name: stack-name
- name: 'Echo Output'run: | echo "stackID: '${{ steps.stack.outputs.stackID }}'" echo "swarmID: '${{ steps.stack.outputs.swarmID }}'" echo "endpointID: '${{ steps.stack.outputs.endpointID }}'"

Examples

View more Examples on the website.

💡 Click on an example heading to expand or collapse the example.

Deploy from a compose file
- name: 'Portainer Deploy'uses: cssnr/portainer-stack-deploy-action@v1with:
token: ${{ secrets.PORTAINER_TOKEN }}url: https://portainer.example.com:9443name: stack-namefile: docker-compose.yamltype: file
Deploy from the repository
- name: 'Portainer Deploy'uses: cssnr/portainer-stack-deploy-action@v1with:
token: ${{ secrets.PORTAINER_TOKEN }}url: https://portainer.example.com:9443name: stack-namefile: docker-compose.yaml
Deploy from a different repository
- name: 'Portainer Deploy'uses: cssnr/portainer-stack-deploy-action@v1with:
token: ${{ secrets.PORTAINER_TOKEN }}url: https://portainer.example.com:9443name: stack-namefile: docker-compose.yamlrepo: https://github.com/user/some-other-reporef: refs/heads/master
Specify environment variables

You can use env_data, env_file, or both.

- name: 'Portainer Deploy'uses: cssnr/portainer-stack-deploy-action@v1with:
token: ${{ secrets.PORTAINER_TOKEN }}url: https://portainer.example.com:9443name: stack-namefile: docker-compose.yamltype: fileenv_data: '{"KEY": "Value"}'env_file: .env
Merging existing environment variables

This will add the provided variables to the existing stack variables.

- name: 'Portainer Deploy'uses: cssnr/portainer-stack-deploy-action@v1with:
token: ${{ secrets.PORTAINER_TOKEN }}url: https://portainer.example.com:9443name: stack-namefile: docker-compose.yamltype: fileenv_data: | KEY: Valuemerge_env: true
Multiline JSON data input

Note: Secrets are secure in this context.

- name: 'Portainer Deploy'uses: cssnr/portainer-stack-deploy-action@v1with:
token: ${{ secrets.PORTAINER_TOKEN }}url: https://portainer.example.com:9443name: stack-namefile: docker-compose.yamltype: fileenv_data: | { "APP_PRIVATE_KEY": "${{ secrets.APP_PRIVATE_KEY }}", "VERSION": "${{ inputs.VERSION }}" }
Only run on release events

This is accomplished by adding an if to the step.

  • if: ${{ github.event_name == 'release' }}
- name: 'Portainer Deploy'uses: cssnr/portainer-stack-deploy-action@v1if: ${{ github.event_name == 'release' }}with:
token: ${{ secrets.PORTAINER_TOKEN }}url: https://portainer.example.com:9443name: stack-namefile: docker-compose.yaml
Deploy with relative path volumes

Portainer Business Edition Only.

- name: 'Portainer Deploy'uses: cssnr/portainer-stack-deploy-action@v1with:
token: ${{ secrets.PORTAINER_TOKEN }}url: https://portainer.example.com:9443name: stack-namefile: docker-compose.yamlfs_path: /mnt
Full build and deploy workflow

This example builds an image, pushes to a registry, then deploys to Portainer.

name: 'Portainer Stack Deploy'on:
workflow_dispatch:
inputs:
tags:
description: 'Tags: comma,separated'required: truedefault: 'latest'env:
REGISTRY: 'ghcr.io'permissions:
contents: readconcurrency:
group: ${{ github.workflow }}cancel-in-progress: truejobs:
build:
name: 'Build'runs-on: ubuntu-latesttimeout-minutes: 15permissions:
packages: writesteps:
- name: 'Checkout'uses: actions/checkout@v5
- name: 'Setup Buildx'uses: docker/setup-buildx-action@v3with:
platforms: 'linux/amd64,linux/arm64'
- name: 'Docker Login'uses: docker/login-action@v3with:
registry: $${{ env.REGISTRY }}username: ${{ secrets.GHCR_USER }}password: ${{ secrets.GHCR_PASS }}
- name: 'Generate Tags'id: tagsuses: cssnr/docker-tags-action@v1with:
images: $${{ env.REGISTRY }}/${{ github.repository }}tags: ${{ inputs.tags }}
- name: 'Build and Push'uses: docker/build-push-action@v6with:
context: .platforms: 'linux/amd64,linux/arm64'push: truetags: ${{ steps.tags.outputs.tags }}labels: ${{ steps.tags.outputs.labels }}deploy:
name: 'Deploy'runs-on: ubuntu-latesttimeout-minutes: 5needs: buildsteps:
- name: 'Checkout'uses: actions/checkout@v5
- name: 'Portainer Deploy'uses: cssnr/portainer-stack-deploy-action@v1with:
token: ${{ secrets.PORTAINER_TOKEN }}url: https://portainer.example.comname: stack-namefile: docker-compose-swarm.yamlcleanup:
name: 'Cleanup'runs-on: ubuntu-latesttimeout-minutes: 5needs: deploysteps:
- name: 'Purge Cache'uses: cssnr/cloudflare-purge-cache-action@v2with:
token: ${{ secrets.CLOUDFLARE_API_TOKEN }}zones: cssnr.com

For more examples, you can check out other projects using this action:
https://github.com/cssnr/portainer-stack-deploy-action/network/dependents

Troubleshooting

  • No such image: ghcr.io/user/repo-name:tag

Make sure your package is not private. If you intend to use a private package, then:
Go to Portainer Registries: https://portainer.example.com/#!/registries/new
Choose Custom registry, set ghcr.io for Registry URL, enable authentication, and add your username/token.

  • Error: Resource not accessible by integration

Only applies to build-push-action or bake-action type actions, not this action.
Permissions can be added on the job or step level with:

permissions:
packages: write

Permissions documentation for Workflows and Actions.

Tags

The following rolling tags are maintained.

Version TagRollingBugsFeat.NameTargetExample
GitHub Tag MajorMajorvN.x.xvN
GitHub Tag MinorMinorvN.N.xvN.N
GitHub ReleaseMicrovN.N.NvN.N.N

You can view the release notes for each version on the releases page.

The Major tag is recommended. It is the most up-to-date and always backwards compatible. Breaking changes would result in a Major version bump. At a minimum you should use a Minor tag.

Support

If you run into any issues or need help getting started, please do one of the following:

FeaturesIssuesDiscussionsDiscord

Contributing

If you would like to submit a PR, please review the CONTRIBUTING.md.

To contribute to the documentation site go to cssnr/portainer-stack-deploy-docs.

Please consider making a donation to support the development of this project and additional open source projects.

Ko-fi

Actions Tools

Additionally, you can support other GitHub Actions I have published:

❔ Unpublished Actions

These actions are not published on the Marketplace, but may be useful.


📝 Template Actions

These are basic action templates that I use for creating new actions.

Note: The docker-test-action builds, runs and pushes images to GitHub Container Registry.


For a full list of current projects visit: https://cssnr.github.io/

Star History Chart

About

Deploy or Update a Portainer Swarm Stack or Standalone Docker Compose Stack Easily from GitHub Actions with Custom Options

Topics

Resources

Contributing

Stars

18 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Used by

Contributors

Languages