Deploy or Update a Portainer Stack from a Repository or Compose File. Supports most features including specifying the repository, compose file, environment variables and much more...
This action is written from the ground up in VanillaJS and is not a fork/clone of existing actions.
No Portainer? You can deploy directly to a docker over ssh with: cssnr/stack-deploy-action
Note
Please submit a Feature Request for new features or Open an Issue if you find any bugs.
This is a fairly simple action, for more details see src/index.js and src/portainer.js.
Input | Req. | Default Value | Input Description |
---|---|---|---|
token |
Yes | - | Portainer Token * |
url |
Yes | - | Portainer URL |
name |
Yes | - | Stack Name |
file |
- | docker-compose.yaml |
Compose File |
endpoint |
- | endpoints[0].Id |
Portainer Endpoint * |
ref |
- | current reference |
Repository Ref * |
repo |
- | current repository |
Repository URL * |
tlsskip |
- | false |
Skip Repo TLS Verify |
prune |
- | true |
Prune Services |
pull |
- | true |
Pull Images |
type |
- | repo |
Type [repo , file ] * |
standalone |
- | false |
Deploy Standalone Stack |
env_json |
- | - | Dotenv JSON Data ** |
env_file |
- | - | Dotenv File Path * |
merge_env |
- | false |
Merge Env Vars * |
username |
- | - | Repository Username * |
password |
- | - | Repository Password * |
fs_path |
- | - | Relative Path (BE) * |
summary |
- | true |
Add Summary to Job * |
For more details on inputs, see the Portainer API documentation.
token: To create a Portainer API token see: https://docs.portainer.io/api/access
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.
ref: If you want to deploy a different ref than the one triggering the workflow.
Useful if you are deploying from another repository. Example: refs/heads/master
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 here.
type: Type of Deployment. Currently, supports either repo
or file
.
env_json/env_file: Optional environment variables used when creating the stack. File should be in dotenv format and
JSON should be an object. Example: {"KEY": "Value"}
Warning
Inputs are NOT secure unless using secrets or secure output.
Using env_json
on a public repository will otherwise expose this data.
To securely pass an environment use the env_file
option.
merge_env: If this is true
and the stack exists, will update the existing Env with the provided env_json/env_file
.
If you are not providing an env, the existing env will be used, and you do not need to set this.
username/password: Only set these if the repo
is private and requires authentication.
This is NOT the Portainer username/password, 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. See the docs for more info.
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
Item | Value |
---|---|
ID | 112 |
Name | test_portainer-stack-deploy |
File | docker-compose.yml |
Type | Swarm |
Status | Active |
Created | 2/28/2025, 3:09:16 AM |
Updated | - |
Path | /data/compose/112 |
EndpointID | 1 |
SwarmID | wr8i8agdr05n6wsf1tkcnhwik |
- name: 'Portainer Deploy'
uses: cssnr/portainer-stack-deploy-action@v1
with:
token: ${{ secrets.PORTAINER_TOKEN }}
url: https://portainer.example.com:9443
name: stack-name
file: docker-compose.yaml
Output | Output Description |
---|---|
stackID | Resulting Stack ID |
swarmID | Resulting Swarm ID |
endpointID | Endpoint ID |
- name: 'Portainer Deploy'
id: stack
uses: cssnr/portainer-stack-deploy-action@v1
with:
token: ${{ secrets.PORTAINER_TOKEN }}
url: https://portainer.example.com:9443
name: stack-name
- name: 'Echo Output'
run: |
echo "stackID: '${{ steps.stack.outputs.stackID }}'"
echo "swarmID: '${{ steps.stack.outputs.swarmID }}'"
echo "endpointID: '${{ steps.stack.outputs.endpointID }}'"
💡 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@v1
with:
token: ${{ secrets.PORTAINER_TOKEN }}
url: https://portainer.example.com:9443
name: stack-name
file: docker-compose.yaml
type: file
Deploy from the repository
- name: 'Portainer Deploy'
uses: cssnr/portainer-stack-deploy-action@v1
with:
token: ${{ secrets.PORTAINER_TOKEN }}
url: https://portainer.example.com:9443
name: stack-name
file: docker-compose.yaml
Deploy from a different repository
- name: 'Portainer Deploy'
uses: cssnr/portainer-stack-deploy-action@v1
with:
token: ${{ secrets.PORTAINER_TOKEN }}
url: https://portainer.example.com:9443
name: stack-name
file: docker-compose.yaml
repo: https://github.com/user/some-other-repo
ref: refs/heads/master
Specify environment variables
You can use env_json, env_file, or both.
- name: 'Portainer Deploy'
uses: cssnr/portainer-stack-deploy-action@v1
with:
token: ${{ secrets.PORTAINER_TOKEN }}
url: https://portainer.example.com:9443
name: stack-name
file: docker-compose.yaml
type: file
env_json: '{"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@v1
with:
token: ${{ secrets.PORTAINER_TOKEN }}
url: https://portainer.example.com:9443
name: stack-name
file: docker-compose.yaml
type: file
env_json: '{"KEY": "Value"}'
merge_env: true
Multiline JSON data input
Note: Secrets are secure in this context.
- name: 'Portainer Deploy'
uses: cssnr/portainer-stack-deploy-action@v1
with:
token: ${{ secrets.PORTAINER_TOKEN }}
url: https://portainer.example.com:9443
name: stack-name
file: docker-compose.yaml
type: file
env_json: |
{
"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@v1
if: ${{ github.event_name == 'release' }}
with:
token: ${{ secrets.PORTAINER_TOKEN }}
url: https://portainer.example.com:9443
name: stack-name
file: docker-compose.yaml
Deploy with relative path volumes
Portainer Business Edition Only.
- name: 'Portainer Deploy'
uses: cssnr/portainer-stack-deploy-action@v1
with:
token: ${{ secrets.PORTAINER_TOKEN }}
url: https://portainer.example.com:9443
name: stack-name
file: docker-compose.yaml
fs_path: /mnt
Full build and deploy workflow
This example builds an image, pushes to a registry, then deploys to Portainer.
name: 'Portainer Stack Deploy Action'
on:
workflow_dispatch:
inputs:
tags:
description: 'Tags: comma,separated'
required: true
default: 'latest'
env:
REGISTRY: 'ghcr.io'
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true
jobs:
build:
name: 'Build'
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
packages: write
steps:
- name: 'Checkout'
uses: actions/checkout@v4
- name: 'Setup Buildx'
uses: docker/setup-buildx-action@v2
with:
platforms: 'linux/amd64,linux/arm64'
- name: 'Docker Login'
uses: docker/login-action@v3
with:
registry: $${{ env.REGISTRY }}
username: ${{ secrets.GHCR_USER }}
password: ${{ secrets.GHCR_PASS }}
- name: 'Generate Tags'
id: tags
uses: cssnr/docker-tags-action@v1
with:
images: $${{ env.REGISTRY }}/${{ github.repository }}
tags: ${{ inputs.tags }}
- name: 'Build and Push'
uses: docker/build-push-action@v6
with:
context: .
platforms: 'linux/amd64,linux/arm64'
push: true
tags: ${{ steps.tags.outputs.tags }}
labels: ${{ steps.tags.outputs.labels }}
deploy:
name: 'Deploy'
runs-on: ubuntu-latest
timeout-minutes: 5
needs: [build]
steps:
- name: 'Checkout'
uses: actions/checkout@v4
- name: 'Portainer Deploy'
uses: cssnr/portainer-stack-deploy-action@v1
with:
token: ${{ secrets.PORTAINER_TOKEN }}
url: https://portainer.example.com
name: stack-name
file: docker-compose-swarm.yaml
cleanup:
name: 'Cleanup'
runs-on: ubuntu-latest
timeout-minutes: 5
needs: [deploy]
permissions:
contents: read
packages: write
steps:
- name: 'Purge Cache'
uses: cssnr/cloudflare-purge-cache-action@v2
with:
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
The following rolling tags are maintained.
Version Tag | Rolling | Bugs | Feat. | Name | Target | Example |
---|---|---|---|---|---|---|
✅ | ✅ | ✅ | Major | vN.x.x |
vN |
|
✅ | ✅ | ❌ | Minor | vN.N.x |
vN.N |
|
❌ | ❌ | ❌ | Micro | vN.N.N |
vN.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.
- 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.
For general help or to request a feature, see:
- Q&A Discussion: https://github.com/cssnr/portainer-stack-deploy-action/discussions/categories/q-a
- Request a Feature: https://github.com/cssnr/portainer-stack-deploy-action/discussions/categories/feature-requests
If you are experiencing an issue/bug or getting unexpected results, you can:
- Report an Issue: https://github.com/cssnr/portainer-stack-deploy-action/issues
- Chat with us on Discord: https://discord.gg/wXy6m2X8wY
- Provide General Feedback: https://cssnr.github.io/feedback/
For more information, see the CSSNR SUPPORT.md.
Currently, the best way to contribute to this project is to star this project on GitHub.
If you would like to submit a PR, please review the CONTRIBUTING.md.
Additionally, you can support other GitHub Actions I have published:
- Stack Deploy Action
- Portainer Stack Deploy
- VirusTotal Action
- Mirror Repository Action
- Update Version Tags Action
- Update JSON Value Action
- Parse Issue Form Action
- Cloudflare Purge Cache Action
- Mozilla Addon Update Action
- Docker Tags Action
- Package Changelog Action
- NPM Outdated Check Action
For a full list of current projects to support visit: https://cssnr.github.io/