Configuration

Authentication

Browser-based SSO login with automatic token refresh:

dtctl auth login --context my-env --environment "https://abc12345.apps.dynatrace.com"

Tokens are stored securely in your OS keyring. To log out:

dtctl auth logout

Token-Based Auth

For CI/CD or headless environments, use a platform API token:

dtctl config set-context my-env \
  --environment "https://abc12345.apps.dynatrace.com" \
  --token-ref my-token

dtctl config set-credentials my-token \
  --token "dt0s16.XXXXXXXX.YYYYYYYY"

Creating a Platform Token

  1. Go to https://myaccount.dynatrace.com/platformTokens (Account Management > My platform tokens)
  2. Select Platform token and fill in name, expiration, account, and environments
  3. Add the required scopes for your use case
  4. Select Generate and copy the token immediately – it’s only shown once

See the Dynatrace Platform Tokens documentation for detailed instructions.

Current User Identity

Check who you’re authenticated as:

dtctl auth whoami

Use dtctl auth whoami -o json for machine-readable output, or --id-only to get just the user ID.

Multiple Environments

Create Contexts

# Development
dtctl config set-context dev \
  --environment "https://dev.apps.dynatrace.com" \
  --token-ref dev-token \
  --safety-level dangerously-unrestricted

# Production (read-only)
dtctl config set-context prod \
  --environment "https://prod.apps.dynatrace.com" \
  --token-ref prod-token \
  --safety-level readonly

Switch Contexts

dtctl config use-context dev

# Or use the shortcut:
dtctl ctx dev

# List all contexts
dtctl ctx

The ctx command is a shorthand for the common config operations. With no argument it lists contexts; with a name it switches. It also has subcommands:

dtctl ctx current                 # Show the current context name
dtctl ctx describe prod           # Show details of a context
dtctl ctx set staging \           # Create or update a context and switch to it
  --environment "https://staging.apps.dynatrace.com" --token-ref staging-token
dtctl ctx delete old-env          # Delete a context
dtctl ctx token prod              # Print the resolved token for a context (defaults to current)

One-Time Context Override

Run a single command against a different context without switching:

dtctl get workflows --context prod

Per-Project Configuration

Create a .dtctl.yaml in your project root for team or CI/CD configuration:

dtctl config init

This generates a template with environment variable placeholders:

apiVersion: dtctl.io/v1
kind: Config
current-context: production
contexts:
  - name: production
    context:
      environment: ${DT_ENVIRONMENT_URL}
      token-ref: my-token
      safety-level: readwrite-all
tokens:
  - name: my-token
    token: ${DT_API_TOKEN}

Commit the file to version control without secrets – each developer or CI system provides values via environment variables.

Config Search Order

  1. --config flag (explicit path)
  2. DTCTL_CONFIG environment variable (explicit path)
  3. .dtctl.yaml in the current directory or any parent (walks up to root)
  4. Global config (~/.config/dtctl/config)

Security: local configs cannot run commands. Because a .dtctl.yaml is auto-discovered by walking up from your current directory, it is treated as untrusted — the same threat model as a checked-out repo, an unpacked tarball, or a shared work dir. dtctl therefore ignores command aliases and apply hooks found in an auto-discovered local .dtctl.yaml, printing a warning to stderr when it does. These code-execution keys are honored only from the global config (~/.config/dtctl/config) or a config you point at explicitly with --config or DTCTL_CONFIG. A local config may still define contexts, tokens, and other preferences. As an additional safeguard, an alias can never shadow a built-in command (e.g. get, apply, version) regardless of where it is defined.

Trusting a prepared workspace with DTCTL_CONFIG

When you control the config file — for example an automation harness that generates a clean working directory with its own .dtctl.yaml, skills, and hooks — export DTCTL_CONFIG so dtctl treats that file as an explicit, trusted config, exactly like --config:

export DTCTL_CONFIG="$PWD/.dtctl.yaml"

This honors the file’s aliases and apply hooks without touching the invocation (an agent can keep running dtctl apply unchanged), and it skips auto-discovery entirely — a stray .dtctl.yaml elsewhere on disk can never shadow the file you named. Because it points at one specific file rather than blanket-trusting whatever is discovered, it does not reopen the untrusted-working-directory threat model above. Point it only at a config you trust, and prefer scoping it to the session (e.g. exported by the harness or a per-repo setup script) rather than your global shell profile.

Safety Levels

Safety levels provide client-side protection against accidental destructive operations:

Level Description
readonly No modifications allowed
readwrite-mine Modify your own resources only
readwrite-all Modify all resources (default)
dangerously-unrestricted All operations including bucket deletion
dtctl config set-context prod \
  --environment "https://prod.apps.dynatrace.com" \
  --token-ref prod-token \
  --safety-level readonly

View context details including safety level:

dtctl config describe-context prod

Safety levels are client-side only. For actual security, configure your API tokens with minimum required scopes.

Apply Hooks

Apply hooks run external commands around dtctl apply:

  • Pre-apply (pre-apply): runs before the resource is sent to the API. Receives the processed JSON on stdin and can reject the apply (non-zero exit aborts).
  • Post-apply (post-apply): runs after a successful apply. Receives the apply result as JSON on stdin. Useful for cleanup, notifications, or writing metadata to disk. A non-zero exit is reported as a warning — the resource is already persisted.

Both hooks are invoked the same way: the command string is tokenized using POSIX-style shell quoting (so "path with spaces" and 'quoted args' are honoured) and executed directly — there is no shell interpretation of the command line itself. The resource type and source file are appended as the final two positional arguments.

Configuration

Hooks are configured globally in preferences or per-context:

# ~/.config/dtctl/config
preferences:
  hooks:
    pre-apply:  "node /opt/dtctl-hooks/validate.js"
    post-apply: "bash /opt/dtctl-hooks/notify.sh"

contexts:
  - name: production
    context:
      environment: https://abc12345.apps.dynatrace.com
      token-ref: prod-token
      hooks:
        pre-apply: "opa eval --bundle /policies -i /dev/stdin"
  - name: dev
    context:
      environment: https://dev.apps.dynatrace.com
      token-ref: dev-token
      hooks:
        pre-apply:  "none"  # explicitly disable the global hook
        post-apply: "none"

Per-context hooks take precedence over global hooks. The special value "none" disables the global hook for a specific context.

Security: hooks are ignored in local configs. Apply hooks are honored only from the global config (~/.config/dtctl/config) or a config named explicitly with --config or DTCTL_CONFIG. Hooks defined in an auto-discovered local .dtctl.yaml (whether in preferences or a context) are ignored, since a local config from an untrusted working directory must not be able to run commands. dtctl prints a warning to stderr when it ignores them. (The hook values remain in the file untouched — they are simply never executed — so editing the config elsewhere never destroys them.) See Config Search Order.

Hook Contract

Aspect Pre-apply Post-apply
Invocation direct exec of the tokenized command, with <resource-type> and <source-file> appended as args same
$1 Resource type (e.g., dashboard, workflow, slo) same
$2 Source filename from -f same
Stdin Processed resource JSON (YAML→JSON + template rendering applied) Apply result JSON (array of per-resource result objects: action, resourceType, id, name, etc.)
Stdout Always forwarded to the user’s stdout Forwarded to the user’s stdout
Stderr Always forwarded to the user’s stderr Always forwarded to the user’s stderr
Exit 0 Proceed with apply Reported as success
Exit non-zero Abort apply, show stdout+stderr Warning only — apply already succeeded; stdout+stderr are shown and a warning line is printed
Timeout 30 seconds 30 seconds
Dry-run Pre-apply runs (validates before the preview) Post-apply is skipped
Array apply (partial failure) Runs once on the full array Runs once on the resources that were persisted, even when later items in the batch fail

Because the command is tokenized but then executed directly (no sh -c), pipes, redirections, glob expansion, and environment-variable expansion in the command string itself are not supported — put any shell logic inside the script the hook invokes.

Agent mode (--agent / -A). When dtctl writes its JSON envelope to stdout, both pre-apply and post-apply hook output is redirected to stderr automatically so the envelope on stdout stays clean for machine consumers. Use stderr for any human-readable hook diagnostics.

Shell positional parameters in config values. The config loader expands $VAR / ${VAR} against the process environment but preserves shell positional parameters ($1, $2, $@, …) verbatim. You can write pre-apply: "bash validate.sh \"$1\" \"$2\"" and have those tokens reach the hook unchanged.

Writing Hooks

Pre-apply — reject dashboards without a title:

#!/bin/bash
# validate.sh
resource_type="$1"

if [ "$resource_type" = "dashboard" ]; then
  title=$(cat | jq -r '.title // empty')
  if [ -z "$title" ]; then
    echo "Error: dashboard must have a title" >&2
    exit 1
  fi
fi

Post-apply — delete the source file after a successful dashboard deploy, forcing the user to re-download before the next edit:

#!/bin/bash
# cleanup.sh
resource_type="$1"
source_file="$2"

if [ "$resource_type" = "dashboard" ] && [ -f "$source_file" ]; then
  rm -- "$source_file"
  id=$(cat | jq -r '.[0].id')
  echo "Deployed dashboard $id. Local file removed; re-download with 'dtctl get dashboard $id' before the next edit."
fi

Usage

dtctl apply -f dashboard.yaml            # pre- and post-apply both run
dtctl apply -f dashboard.yaml --no-hooks # skip both hooks
dtctl apply -f dashboard.yaml --dry-run  # pre-apply runs, post-apply is skipped
dtctl apply -f dashboard.yaml -v         # verbose: logs hook command and duration

Result Spill

dtctl query can spill a large result to a local file and return a compact summary instead of the rows. Defaults can be set globally or per-context under a spill: section:

# ~/.config/dtctl/config  (global, or under a specific context)
spill:
  mode: auto            # auto | always | never  (overrides the agent/non-agent default)
  dir: ~/.cache/dtctl/results   # base directory for spilled files
  format: jsonl         # jsonl | json | csv | parquet
  threshold: 50KB       # serialised output size that triggers a spill
  ttl: 24h              # how long spilled files are kept before pruning

Environment overrides (handy for containers/CI):

export DTCTL_SPILL=never                 # auto | always | never — kill switch for disk writes
export DTCTL_SPILL_DIR=/mnt/scratch      # write spills to a mounted volume

Precedence (highest wins): flag → environment → context config → global config → built-in default. A user-chosen dir (or DTCTL_SPILL_DIR / --spill-to) is written outside the managed cache and opts out of its TTL pruning and per-context partitioning — you own that file’s lifetime.

Command Aliases

Create shortcuts for frequently used commands.

Simple Aliases

dtctl alias set wf "get workflows"
dtctl wf
# Expands to: dtctl get workflows

Parameterized Aliases

Use $1-$9 for positional parameters:

dtctl alias set logs-errors "query 'fetch logs | filter status=\$1 | limit 100'"
dtctl logs-errors ERROR
# Expands to: dtctl query 'fetch logs | filter status=ERROR | limit 100'

Shell Aliases

Prefix with ! to execute through the system shell (enables pipes and external tools):

dtctl alias set wf-names "!dtctl get workflows -o json | jq -r '.workflows[].title'"
dtctl wf-names

Import and Export

Share aliases with your team:

dtctl alias export -f team-aliases.yaml
dtctl alias import -f team-aliases.yaml

Managing Aliases

dtctl alias list         # List all aliases
dtctl alias delete wf    # Delete an alias

Alias Safety

Aliases cannot shadow built-in commands:

dtctl alias set get "query 'fetch logs'"
# Error: alias name "get" conflicts with built-in command

This guard is also enforced at resolution time: even an alias written directly into a config file (bypassing alias set) can never override a built-in — dtctl ignores it and runs the real command, warning on stderr.

Aliases are honored only from the global config or a config named explicitly with --config or DTCTL_CONFIG. Aliases in an auto-discovered local .dtctl.yaml are ignored for security (see Config Search Order), so alias export / import should target the global config, not a per-project file.


Previous: Quick Start