Core Patterns
Secret Reference Format
op://<vault>/<item>/<field>
Examples:
op://Development/AWS/access_key_id op://Production/Database/password op://Shared/Stripe/secret_key
Item Naming Conventions
Format: {environment}-{service} with kebab-case. One item per environment.
Pattern Example Bad Alternative
{env}-{service}
production-rails
Production Rails
{env}-{provider}
production-dockerhub
API Key
{env}-{provider}-{resource}
production-hetzner-s3
Mixed env items
Field Naming
Use semantic field names that describe the credential type:
Good Bad Why
access_token
value
Self-documenting
master_key
secret
Specific purpose clear
secret_access_key
key
Matches AWS naming
api_token
token
Distinguishes from other tokens
Field naming rules:
-
Match the provider's terminology when possible (AWS uses access_key_id , secret_access_key )
-
Use snake_case for consistency
-
Be specific: database_password not just password when item has multiple credentials
Environment File (.op.env)
Create .op.env in project root:
AWS_ACCESS_KEY_ID=op://Infrastructure/AWS/access_key_id AWS_SECRET_ACCESS_KEY=op://Infrastructure/AWS/secret_access_key DIGITALOCEAN_TOKEN=op://Infrastructure/DigitalOcean/api_token DATABASE_URL=op://Production/PostgreSQL/connection_string STRIPE_SECRET_KEY=op://Production/Stripe/secret_key
Critical: Add to .gitignore :
1Password - NEVER commit
.op.env *.op.env
Running Commands with Secrets
op run --env-file=.op.env -- terraform plan op run --env-file=.op.env -- rails server
Integration Patterns
Makefile Integration
OP ?= op OP_ENV_FILE ?= .op.env
Prefix for all commands needing secrets
CMD = $(OP) run --env-file=$(OP_ENV_FILE) --
deploy: $(CMD) kamal deploy
console: $(CMD) rails console
migrate: $(CMD) rails db:migrate
Docker Compose
op run --env-file=.op.env -- docker compose up
Kamal Deployment
config/deploy.yml
env: secret: - RAILS_MASTER_KEY - DATABASE_URL - REDIS_URL
.kamal/secrets
RAILS_MASTER_KEY=$(op read "op://Production/Rails/master_key") DATABASE_URL=$(op read "op://Production/PostgreSQL/url") REDIS_URL=$(op read "op://Production/Redis/url")
CI/CD (GitHub Actions)
.github/workflows/deploy.yml
jobs: deploy: runs-on: ubuntu-latest steps: - uses: 1password/load-secrets-action@v2 with: export-env: true env: OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} AWS_ACCESS_KEY_ID: op://CI/AWS/access_key_id AWS_SECRET_ACCESS_KEY: op://CI/AWS/secret_access_key
- run: terraform apply -auto-approve
CLI Commands
Reading Secrets
op read "op://Vault/Item/field" op read "op://Vault/Item/field" --format json op read "op://Vault/TLS/private_key" > /tmp/key.pem && chmod 600 /tmp/key.pem
Injecting into Commands
DATABASE_URL=$(op read "op://Production/DB/url") rails db:migrate op run --env-file=.op.env -- ./deploy.sh op run --account my-team --env-file=.op.env -- terraform apply
Managing Items
op vault list
op item list --vault Infrastructure
op item get "AWS" --vault Infrastructure
op item create --category login --vault Infrastructure
--title "New Service" --field username=admin --field password=secret123
Project Setup
Initial Configuration
op signin op vault list
cat > .op.env << 'EOF' AWS_ACCESS_KEY_ID=op://Infrastructure/AWS/access_key_id AWS_SECRET_ACCESS_KEY=op://Infrastructure/AWS/secret_access_key DATABASE_URL=op://Production/Database/url REDIS_URL=op://Production/Redis/url EOF
op run --env-file=.op.env -- env | grep -E '^(AWS|DATABASE|REDIS)'
Placeholder Workflow
Create items with placeholder values upfront, populate with real credentials later:
op item create --vault myproject --category login
--title "production-rails" --field master_key="PLACEHOLDER_UPDATE_BEFORE_DEPLOY"
cat > .kamal/secrets << 'EOF' RAILS_MASTER_KEY=$(op read "op://myproject/production-rails/master_key") EOF
Later: update with real value
op item edit "production-rails" --vault myproject
master_key="actual_secret_value_here"
Vault Organization
Single-Vault Approach (Simpler)
Use one vault with naming conventions for environment separation:
Vault: myproject Items:
- production-rails
- production-dockerhub
- production-hetzner-s3
- staging-rails
- staging-dockerhub
- development-rails
Multi-Vault Approach (Team Scale) -- use when teams need different access controls:
Vault Purpose Access
Infrastructure
Cloud provider credentials DevOps team
Production
Production app secrets Deploy systems
Staging
Staging environment Dev team
Development
Local dev secrets Individual devs
Security Rules
-
Add .op.env and *.op.env to .gitignore -- never commit
-
Use service accounts for CI/CD, not personal accounts
-
Never pipe op read to logs or echo
-
Never store session tokens in scripts
-
Use variables for vault/item names in automation
Troubleshooting
op signin # Re-authenticate expired session op whoami # Check current session op vault list # Verify vault access op item list --vault Infrastructure | grep -i aws # Search for items op item get "AWS" --vault Infrastructure --format json | jq '.fields[].label' # Check field names
Multiple Accounts
Always specify account in automation -- never rely on "last signed in":
op vault list --account acme.1password.com export OP_ACCOUNT=acme.1password.com op run --account acme.1password.com --env-file=.op.env -- ./deploy.sh
Multi-Environment Pattern
Use per-environment env files: .op.env.production , .op.env.staging , .op.env.development
ENV ?= development OP_ENV_FILE = .op.env.$(ENV)
deploy: op run --env-file=$(OP_ENV_FILE) -- kamal deploy
Usage: make deploy ENV=production
References
- references/multiple-accounts.md - Cross-account workflows and Makefile integration