Octopus Deploy CLI & API — Setup and Usage Guide¶
1. Install the Octopus CLI¶
The modern CLI is simply called octopus (older versions were octo — if you see octo referenced anywhere, that's the legacy tool, deprecated in favor of this one).
Linux (including WSL, matching your usual setup):
# via apt (Debian/Ubuntu)
curl -sSfL https://apt.octopus.com/public.key | sudo apt-key add -
sudo sh -c 'echo "deb https://apt.octopus.com/ any main" > /etc/apt/sources.list.d/octopus.com.list'
sudo apt-get update
sudo apt-get install octopuscli
Or via direct binary download (avoids apt-key deprecation warnings on newer Ubuntu):
curl -L -o octopuscli.tar.gz https://download.octopusdeploy.com/octopus-tools/9.1.7/OctopusTools.9.1.7.linux-x64.tar.gz
mkdir -p ~/bin/octopus-cli
tar -xzf octopuscli.tar.gz -C ~/bin/octopus-cli
echo 'export PATH="$HOME/bin/octopus-cli:$PATH"' >> ~/.bashrc
source ~/.bashrc
Check octopus.com/downloads/octopuscli for the current version number before pulling — 9.1.7 is illustrative, confirm the latest.
Verify install:
octopus version
2. Create an API key¶
- Log into your Octopus instance in the browser
- Click your profile icon (top right) → Profile
- Go to the My API Keys tab
- Click New API Key
- Give it a purpose-specific name (e.g.
andrew-cli-nginx-audit) — makes it easy to identify and revoke later without guessing what it was for - Set an expiry if your org's policy requires it (recommended regardless — don't create a permanent key for a one-off task)
- Copy the key immediately — like most API key flows, Octopus shows it once and won't let you retrieve it again
Store it properly, matching your usual credential handling:
pass insert octopus/api-key
3. Authenticate the CLI¶
Two ways to supply credentials — env vars (good for scripting/CI) or per-command flags.
Environment variables (recommended for interactive use):
export OCTOPUS_URL="https://your-instance.octopus.app"
export OCTOPUS_API_KEY="$(pass octopus/api-key)"
Add these to ~/.bashrc if you'll use the CLI regularly, or keep them in a small .env-style script you source per session if you'd rather not have the key sitting in shell history/environment persistently.
Per-command flags (explicit, no env dependency):
octopus project list --url "https://your-instance.octopus.app" --api-key "$(pass octopus/api-key)"
Test the connection:
octopus space list
If this returns your Octopus space(s) without error, auth is working.
4. Core commands you'll actually use¶
List/inspect resources¶
octopus project list
octopus environment list
octopus release list --project "infrastructure-logging"
octopus deployment list --project "infrastructure-logging" --environment "Dev"
Working with variables¶
The CLI's variable management is more limited than the API directly — for reading/writing project variables (like your auth_basic_user_file_base64 case), you'll often need the raw REST API rather than a dedicated CLI subcommand, since sensitive variables in particular have limited CLI support. See section 5.
Create a release¶
octopus release create \
--project "infrastructure-logging" \
--version "1.2.3+logstash.1" \
--channel "logstash" \
--package-version "1.2.3"
Deploy a release¶
octopus release deploy \
--project "infrastructure-logging" \
--version "1.2.3+logstash.1" \
--environment "Dev" \
--progress
--progress streams live deployment log output to your terminal instead of just firing and returning immediately.
Check deployment status¶
octopus deployment list --project "infrastructure-logging" --environment "Dev" --latest
5. Using the raw REST API directly (needed for sensitive variables)¶
The CLI wraps the REST API, but not every operation has full CLI coverage — sensitive variable read/write is one of those gaps. For those cases, hit the API directly with curl or Terraform's Octopus provider (per your earlier question).
Base pattern:
curl -s -H "X-Octopus-ApiKey: $(pass octopus/api-key)" \
"https://your-instance.octopus.app/api/spaces/Spaces-1/projects" | jq
Note the Spaces-1 — most Octopus instances default to a single space with this ID, but confirm yours:
curl -s -H "X-Octopus-ApiKey: $(pass octopus/api-key)" \
"https://your-instance.octopus.app/api/spaces" | jq '.Items[] | {Id, Name}'
Get a project's ID (needed for most subsequent calls):
curl -s -H "X-Octopus-ApiKey: $(pass octopus/api-key)" \
"https://your-instance.octopus.app/api/spaces/Spaces-1/projects?partialName=infrastructure-logging" | jq '.Items[] | {Id, Name}'
Get the project's variable set:
PROJECT_ID="Projects-123" # from above
curl -s -H "X-Octopus-ApiKey: $(pass octopus/api-key)" \
"https://your-instance.octopus.app/api/spaces/Spaces-1/projects/$PROJECT_ID" | jq -r '.VariableSetId'
Read the variable set (sensitive values will show as null/masked — API won't return them, same limitation as the UI):
VARSET_ID="variableset-Projects-123"
curl -s -H "X-Octopus-ApiKey: $(pass octopus/api-key)" \
"https://your-instance.octopus.app/api/spaces/Spaces-1/variables/$VARSET_ID" | jq '.Variables[] | select(.Name | contains("auth_basic_user_file"))'
Update a sensitive variable's value:
This requires a full PUT of the variable set with the updated value — you can't PATCH a single variable in place via API any more than you can via UI:
# 1. Pull the current variable set JSON
curl -s -H "X-Octopus-ApiKey: $(pass octopus/api-key)" \
"https://your-instance.octopus.app/api/spaces/Spaces-1/variables/$VARSET_ID" > current-varset.json
# 2. Edit the target variable's value in the JSON (jq example below updates in place)
NEW_VALUE=$(cat nginx-users-dev.b64)
jq --arg newval "$NEW_VALUE" '
(.Variables[] | select(.Name == "k8s.containers.logstash.secrets.auth_basic_user_file_base64" and .Scope.Environment[0] == "Environments-Dev-ID") | .Value) = $newval
' current-varset.json > updated-varset.json
# 3. PUT the updated set back
curl -s -X PUT \
-H "X-Octopus-ApiKey: $(pass octopus/api-key)" \
-H "Content-Type: application/json" \
-d @updated-varset.json \
"https://your-instance.octopus.app/api/spaces/Spaces-1/variables/$VARSET_ID"
Important caveats on this jq step:
- The exact Scope.Environment matching depends on your variable's actual scope structure — inspect current-varset.json first to get the right environment ID and JSON path before trusting this blindly, since scope structures vary (some variables are unscoped, some scoped to multiple environments/roles).
- Since this is a full-set PUT, any concurrent edit by someone else in the UI between your GET and PUT will get clobbered — low risk for a solo task, but worth being aware of for anything with more than one person touching variables.
- Double-check the variable's IsSensitive: true flag stays intact in your edited JSON — don't accidentally flip it while editing.
6. Wrapping this into a script (optional, ties everything together)¶
#!/bin/bash
set -euo pipefail
export OCTOPUS_URL="https://your-instance.octopus.app"
export OCTOPUS_API_KEY="$(pass octopus/api-key)"
PROJECT="infrastructure-logging"
ENVIRONMENT="Dev"
VERSION="1.2.3+logstash.$(date +%s | tail -c 4)" # or your own increment logic
echo "Creating release $VERSION..."
octopus release create --project "$PROJECT" --version "$VERSION" --channel "logstash"
echo "Deploying to $ENVIRONMENT..."
octopus release deploy --project "$PROJECT" --version "$VERSION" --environment "$ENVIRONMENT" --progress
echo "Done. Verify with curl against the logging endpoint."
7. Key gotchas to remember¶
- CLI and API can't read sensitive variable values back — same restriction as the UI, this is by Octopus design, not a tooling gap.
- Full-set PUT, not per-variable PATCH — any variable update via API touches the whole variable set JSON; be careful not to clobber unrelated variables.
- Release version snapshots variables at creation, not deploy — relevant to your earlier rollback question; the CLI/API follow the same behavior as the UI here.
- API key expiry — if you set one,
octopuscommands will start failing with 401s once it lapses; worth noting the expiry date somewhere so it's not a surprise mid-task.
Want a version of the earlier "pull/backup/edit/push" command list rewritten to use the CLI/API instead of manual UI steps for the Octopus portion?