How do you configure Bitbucket repository variables at different scopes and manage secrets for multi-environment deployments?
Quick Answer
Repository variables exist at three scopes: workspace (shared across all repos), repository (single repo), and deployment environment (per environment like staging/production). Variables follow a precedence hierarchy where the most specific scope wins, and secured variables are encrypted at rest, masked in logs, and unavailable to fork pipelines.
Detailed Answer
Think of variable scoping like a set of Russian nesting dolls. The outermost doll (workspace variables) holds defaults that apply everywhere. The middle doll (repository variables) can override those defaults for a specific repo. The innermost doll (deployment variables) overrides everything for a specific environment. When you open the dolls, the smallest one wins because it is the most specific.
Bitbucket Pipelines variable scoping follows a clear hierarchy designed for multi-environment deployments. Workspace variables are set in Workspace Settings > Pipelines > Workspace Variables and are accessible to every repository in the workspace. This is ideal for organization-wide secrets like Docker registry credentials or shared API keys. Repository variables are set in Repository Settings > Pipelines > Repository Variables and apply only to that repository. Deployment variables are tied to a specific deployment environment (test, staging, production) and are only injected when a step references that environment with the 'deployment' keyword.
The precedence resolution is deterministic: deployment variables override repository variables, which override workspace variables. If DATABASE_URL is defined at all three levels, a step with 'deployment: production' uses the production deployment value, a step with 'deployment: staging' uses the staging value, and a step without a deployment keyword uses the repository-level value (or workspace-level if no repository-level exists). Secured variables at any level are encrypted using AES-256, cannot be read back from the UI after creation, and are masked in build logs by replacing their values with asterisks. However, masking is not foolproof: encoding the value (base64, hex) can bypass the mask.
In production, a mature variable management strategy segregates secrets by blast radius. Workspace-level variables hold only truly shared secrets like the Artifactory password used by all repositories. Repository-level variables hold service-specific configuration like the service's own Datadog API key. Deployment variables hold environment-specific infrastructure details like database connection strings, cloud account IDs, and endpoint URLs. This layering means a developer with access to the test environment cannot see production database credentials because those are scoped to the production deployment environment and only accessible to pipelines targeting production.
A gotcha that causes security incidents: when a pipeline step does not specify a deployment environment, deployment-scoped variables are not injected. If a developer removes the 'deployment: production' keyword from a step but the script still references $DATABASE_URL, the variable will be empty (or fall back to a repository-level value that might point to a different database). This silent fallback can cause production steps to accidentally write to staging databases. Always validate critical environment variables at the beginning of deployment scripts with explicit checks.