Skip to main content

CI/CD integration patterns

The standard pattern for Passwork CI/CD integration:

Pipeline runner → passwork-cli exec → deploy/build script with secrets in ENV

Only three Passwork credentials live in the CI/CD platform's own secret storage: PASSWORK_HOST, PASSWORK_TOKEN, and PASSWORK_MASTER_KEY. Everything else — database passwords, API keys, cloud credentials — lives in Passwork and is injected at runtime.


Design principles​

One service account per pipeline type​

A production deploy pipeline and a staging deploy pipeline should use different service accounts with access to their respective environments. This limits blast radius if a token is leaked.

Read only as the default access level​

Grant Read only to all service accounts unless the pipeline explicitly rotates credentials, in which case Read and edit is needed.

Never echo secrets​

CI/CD platforms capture stdout and stderr in job logs. Never echo $SECRET or printenv | grep SECRET in pipeline steps. Use secrets only as arguments to commands.

Use folder ID, not item IDs​

Structure your vault so a pipeline can use --folder-id to get all its secrets from one place. Hardcoding individual --password-id values is only justified when you need a single specific credential.


GitLab CI​

Setup​

In Settings → CI/CD → Variables, add (all set to Protected and Masked where applicable):

VariableTypeMasked
PASSWORK_HOSTVariableNo
PASSWORK_TOKENVariableYes
PASSWORK_MASTER_KEYVariableYes
PROD_SECRETS_FOLDER_IDVariableNo
STAGING_SECRETS_FOLDER_IDVariableNo

Basic pipeline​

stages:
- deploy

deploy_production:
stage: deploy
image: passwork/passwork-cli:latest
variables:
PASSWORK_HOST: $PASSWORK_HOST
PASSWORK_TOKEN: $PASSWORK_TOKEN
PASSWORK_MASTER_KEY: $PASSWORK_MASTER_KEY
script:
- passwork-cli exec --folder-id "$PROD_SECRETS_FOLDER_ID" ./deploy.sh
environment:
name: production
when: manual
only:
- main

Multi-environment pipeline​

.deploy_template: &deploy_template
image: passwork/passwork-cli:latest
variables:
PASSWORK_HOST: $PASSWORK_HOST
PASSWORK_TOKEN: $PASSWORK_TOKEN
PASSWORK_MASTER_KEY: $PASSWORK_MASTER_KEY
script:
- passwork-cli exec --folder-id "$SECRETS_FOLDER_ID" ./deploy.sh

deploy_staging:
<<: *deploy_template
stage: deploy
variables:
SECRETS_FOLDER_ID: $STAGING_SECRETS_FOLDER_ID
environment:
name: staging
only:
- develop

deploy_production:
<<: *deploy_template
stage: deploy
variables:
SECRETS_FOLDER_ID: $PROD_SECRETS_FOLDER_ID
environment:
name: production
when: manual
only:
- main

GitHub Actions​

Setup​

In Settings → Secrets and variables → Actions:

  • Secrets (encrypted): PASSWORK_HOST, PASSWORK_TOKEN, PASSWORK_MASTER_KEY
  • Variables (plain): PROD_SECRETS_FOLDER_ID, STAGING_SECRETS_FOLDER_ID

Basic workflow​

name: Deploy

on:
push:
branches: [main]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Deploy with secrets from Passwork
run: |
docker run --rm \
-e PASSWORK_HOST="${{ secrets.PASSWORK_HOST }}" \
-e PASSWORK_TOKEN="${{ secrets.PASSWORK_TOKEN }}" \
-e PASSWORK_MASTER_KEY="${{ secrets.PASSWORK_MASTER_KEY }}" \
-v ${{ github.workspace }}:/app \
-w /app \
passwork/passwork-cli:latest \
exec --folder-id "${{ vars.PROD_SECRETS_FOLDER_ID }}" ./deploy.sh

Multi-environment workflow​

jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}
steps:
- uses: actions/checkout@v4

- name: Deploy
run: |
docker run --rm \
-e PASSWORK_HOST="${{ secrets.PASSWORK_HOST }}" \
-e PASSWORK_TOKEN="${{ secrets.PASSWORK_TOKEN }}" \
-e PASSWORK_MASTER_KEY="${{ secrets.PASSWORK_MASTER_KEY }}" \
-v ${{ github.workspace }}:/app \
-w /app \
passwork/passwork-cli:latest \
exec --folder-id "${{ vars.SECRETS_FOLDER_ID }}" ./deploy.sh

Bitbucket Pipelines​

image: passwork/passwork-cli:latest

pipelines:
branches:
main:
- step:
name: Deploy to production
deployment: production
script:
- passwork-cli exec --folder-id "$PROD_SECRETS_FOLDER_ID" ./deploy.sh

Configure PASSWORK_HOST, PASSWORK_TOKEN, PASSWORK_MASTER_KEY, and folder IDs in Repository settings → Pipelines → Repository variables (mark the tokens as Secured).


Kubernetes​

Init container pattern​

An init container fetches secrets before the main application starts and writes them to a shared in-memory volume. The main container reads from that volume.

apiVersion: v1
kind: Pod
metadata:
name: order-service
spec:
initContainers:
- name: fetch-secrets
image: passwork/passwork-cli:latest
env:
- name: PASSWORK_HOST
valueFrom:
secretKeyRef:
name: passwork-credentials
key: host
- name: PASSWORK_TOKEN
valueFrom:
secretKeyRef:
name: passwork-credentials
key: token
- name: PASSWORK_MASTER_KEY
valueFrom:
secretKeyRef:
name: passwork-credentials
key: master-key
command:
- sh
- -c
- passwork-cli exec --folder-id "$SECRETS_FOLDER_ID" env > /secrets/.env
volumeMounts:
- name: secrets-volume
mountPath: /secrets

containers:
- name: app
image: order-service:latest
command:
- sh
- -c
- |
set -a && source /secrets/.env && set +a
exec ./app
volumeMounts:
- name: secrets-volume
mountPath: /secrets
readOnly: true

volumes:
- name: secrets-volume
emptyDir:
medium: Memory # in-memory only — not written to disk

The Kubernetes Secret passwork-credentials holds only the Passwork bootstrap tokens.

Sidecar pattern for periodic refresh​

When secrets rotate frequently, a sidecar container refreshes the .env file without restarting the pod:

  containers:
- name: app
image: order-service:latest
# app reloads /secrets/.env when it detects a change (via inotify or polling)

- name: secrets-sync
image: passwork/passwork-cli:latest
env:
- name: PASSWORK_HOST
valueFrom:
secretKeyRef:
name: passwork-credentials
key: host
- name: PASSWORK_TOKEN
valueFrom:
secretKeyRef:
name: passwork-credentials
key: token
- name: PASSWORK_MASTER_KEY
valueFrom:
secretKeyRef:
name: passwork-credentials
key: master-key
command:
- sh
- -c
- |
while true; do
passwork-cli exec --folder-id "$SECRETS_FOLDER_ID" env > /secrets/.env.new
mv /secrets/.env.new /secrets/.env
sleep 300
done
volumeMounts:
- name: secrets-volume
mountPath: /secrets

Audit trail​

Every secret access by a service account is recorded in Passwork's action history under the service account's identity. This gives you a complete, centralized audit trail:

  • Which pipeline fetched which credentials and when
  • Which rotation script updated which item
  • Any access outside expected patterns (alert candidates)

Navigate to Settings → Activity log and filter by the service account username to review its activity.

For full CI/CD integration reference, see CI/CD and infrastructure integrations.