Metadata-Version: 2.5
Name: portainer-redeploy-cli
Version: 0.2.0
Summary: Redeploy Portainer stacks from the terminal, with a version check and a live rollout view
Project-URL: Repository, https://gitlab.com/alexandra.tapkova/portainer-redeploy-cli
Author-email: Alexandra Ťapková <git@ouppy.space>
License: AGPL-3.0-or-later
License-File: LICENSE
Keywords: cli,compose,deploy,devops,docker,portainer,swarm
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: System :: Software Distribution
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.9
Requires-Dist: expandvars>=1.0
Requires-Dist: httpx>=0.24
Requires-Dist: pyyaml>=5.1
Requires-Dist: rich>=13
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: twine; extra == 'dev'
Description-Content-Type: text/markdown

# portainer-redeploy-cli

Redeploy Portainer stacks from the terminal.

When you give a version, the tool first checks that the new images exist. It pulls them through Portainer, with the registry credentials that Portainer already has. A mistyped tag therefore stops the run before the tool changes the stack. Then the tool shows the rollout live until the stack settles. If a container becomes unhealthy, the tool reports a failure.

```
════════════════════════════════════════════════════════════════════════════════
  Portainer Redeploy  ·  shop → 1.4.2  ·  prod

  ✓  Stack shop  ·  compose  ·  environment docker-prod
  ▶  IMAGE_TAG  1.4.1 → 1.4.2

    ── images
    ✓  registry.gitlab.com/acme/shop/app:1.4.2    downloaded newer image
    ✓  registry.gitlab.com/acme/shop/nginx:1.4.2  image is up to date

    ── rollout
    ✓  app           1/1 replaced    healthy
    -  migrate       1/1 replaced    exited (0)
    ✓  nginx         1/1 replaced    running
    ✓  redis         0/1 replaced    running

  ✓  Redeployed shop  ·  3/4 containers replaced  ·  3/4 running  ·  41s
```

## Install

```
pipx install portainer-redeploy-cli
```

To install from a source checkout, run `pipx install .`

The tool needs Python 3.9 or later. You can run it using the `redeploy` command.

## Usage

```
redeploy [options] [INSTANCE] STACK [VERSION]
```

```
redeploy prod shop                      # redeploy "shop" on the "prod" instance and pull its images again
redeploy prod shop 1.4.2                # the same, but set IMAGE_TAG to 1.4.2
redeploy shop 1.4.2                     # you can omit INSTANCE when only one instance is configured
redeploy --stop-start prod shop 1.4.2   # stop "shop" first, then redeploy it
```

When you give a `VERSION`, the tool sets the stack's `IMAGE_TAG` variable to it. If the stack does not have that variable yet, the tool adds it. To set a different variable, use `tag_var` in the config file or the `PORTAINER_TAG_VAR` environment variable.

Before the redeploy, the tool checks the images:

1. It finds every image in the stack file that uses the tag variable (`IMAGE_TAG` or the variable that you set).
2. It expands the variables in each image name the same way compose does (`${IMAGE_TAG}-nginx`, `${REGISTRY:-…}/app:$IMAGE_TAG`, YAML anchors, …).
3. It pulls each image at the new version onto the stack's environment.

If a pull fails, the tool does not redeploy the stack. If the pulls pass, the redeploy has less to download.

Then the tool tells Portainer to redeploy the stack and to pull its images again. It watches the containers until the rollout settles. The rollout settles when all of these are true:

- The deploy is done.
- The set of containers stops changing.
- No container is still starting.

Services that are one-off jobs may exit.

On a swarm stack, the deploy can fail with "update out of sequence". Swarm gives this error when a service changes during the deploy, for example when swarm-cronjob starts a job. The deploy stops at that service, so other services can stay on the old version. In this case, the tool waits until swarm completes its rollouts of the stack (at least 10 seconds, at most one minute) and tries again. The tool tries at most 3 times. If you press Ctrl-C while the tool waits, it does not try again.

The redeploy of a large stack can fail, for example when swarm gives "update out of sequence" on all 3 tries. In this case, use `--stop-start`. With this option, the tool stops the stack after the image check. Then it waits until Portainer reports the stack as stopped and no container of the stack runs. Then it redeploys the stack. Portainer creates the services and containers of the stack again, and does not update them. If the stack is already stopped, the tool does not stop it.

`--stop-start` has these effects:

- The stack is down from the stop until the new containers start.
- The stop keeps the named volumes of the stack. The new containers do not get the data in anonymous volumes.
- If the stop or the redeploy fails, the stack can stay stopped or only partly running. This is also true when you press Ctrl-C between the stop and the redeploy. To start the stack again, run the tool again.

Exit codes:

- `0`: the stack settled, and nothing failed.
- `1`: any failure, including unhealthy containers and timeouts.
- `130`: you pressed Ctrl-C.

If you press Ctrl-C after the redeploy starts, the tool only stops watching. Portainer continues the redeploy.

When the output does not go to a terminal, the tool writes plain text without colors or animation. This keeps CI logs easy to read. `NO_COLOR` also disables colors on a terminal.

## Setup

You need a Portainer API key. To create one in Portainer, go to *My account › Access tokens › Add access token*. The user of the key needs access to the stacks that you redeploy.

**One instance:** set `PORTAINER_URL` and `PORTAINER_API_KEY` in your environment.

**Several instances:** describe them in `~/.config/portainer-redeploy.json`, as in the example below. Give the instance name as the first argument.

```json
{
  "prod": {
    "url": "https://portainer.example.com",
    "api_key_file": "/run/secrets/portainer_prod_api_key"
  },
  "lab": {
    "url_file": "/run/secrets/portainer_lab_url",
    "api_key": "ptr_…",
    "insecure": true,
    "tag_var": "APP_VERSION"
  }
}
```

| Key | Description |
|---|---|
| `url` or `url_file` | Portainer base URL, or a file that contains it |
| `api_key` or `api_key_file` | API key, or a file that contains it. A file keeps the key out of the config file and out of your shell's environment. It also works with sops-nix, agenix, Docker secrets and similar tools |
| `insecure` | `true` skips TLS certificate verification. Use it for instances with self-signed certificates on a network that you trust |
| `tag_var` | Stack variable that the tool sets to `VERSION` on this instance (default: `IMAGE_TAG`) |

The tool always reads a named instance from the config file. If you do not name an instance, the tool uses `PORTAINER_URL` when it is set. Otherwise, it uses the only instance in the config file.

## Options

| Option | Description |
|---|---|
| `-i`, `--instance NAME` | Instance from the config file. This is the same as giving the instance as the first argument |
| `-E`, `--environment NAME\|ID` | Portainer environment that has the stack. Use it when stacks in several environments have the same name |
| `--timeout SECONDS` | How long to wait for the rollout to settle (default: `600`). With `--stop-start`, it is also the time limit for the stop |
| `-k`, `--insecure` | Skip TLS certificate verification |
| `--stop-start` | Stop the stack before the redeploy. Then the redeploy starts the stack again. Use it when the redeploy of a large stack fails. Refer to [Usage](#usage) |

## Environment variables

| Variable | Description |
|---|---|
| `PORTAINER_URL` | Portainer base URL. The tool uses it when you do not name an instance |
| `PORTAINER_API_KEY` | API key for `PORTAINER_URL` |
| `PORTAINER_INSECURE` | `1` skips TLS certificate verification for `PORTAINER_URL` |
| `PORTAINER_TAG_VAR` | Stack variable that the tool sets to `VERSION` for `PORTAINER_URL` (default: `IMAGE_TAG`) |
| `PORTAINER_REDEPLOY_CONFIG` | Config file to use instead of `~/.config/portainer-redeploy.json` |
| `NO_COLOR` | Disables colors |

## Compatibility

The tool works with Portainer CE and BE 2.x. It supports compose and swarm stacks, both file-based stacks and stacks from git. The tool does not support Kubernetes stacks.

For git stacks, the tool uses Portainer's git redeploy, so the stacks stay linked to their repository. The git redeploy pulls the latest commit of the stack's branch.

Portainer 2.36 and later deploy in the background. The tool follows the deploy status. If the deploy fails, the tool shows the error from Portainer. Older versions of Portainer reply only when the deploy is done. If a reverse proxy in front of Portainer stops waiting for such a long request, the tool still follows the deploy.

## License

AGPL-3.0-or-later