Local File Targets¶
Local file targets store secrets in files on the local filesystem, making them ideal for development environments, testing, and applications that read configuration from files.
Overview¶
The file target supports multiple formats commonly used for configuration:
.env(dotenv) - Environment variable format- JSON - JavaScript Object Notation
- YAML - YAML Ain't Markup Language
- TOML - Tom's Obvious Minimal Language
Use Cases¶
- Local development - Store secrets for development environments
- Container configuration - Mount secret files in Docker containers
- Legacy applications - Support apps that read from config files
- Backup - Create local copies of cloud-managed secrets
- Testing - Generate test fixtures with realistic secret data
Configuration¶
Basic Example¶
secrets:
- name: database_password
kind: random_password
config:
length: 32
targets:
- provider: local
kind: file
config:
path: .env
format: dotenv
merge: true
Configuration Options¶
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
path |
string | Yes | .env |
File path to store secrets (relative or absolute) |
format |
string | Yes | dotenv |
File format: dotenv, json, yaml, toml |
merge |
boolean | No | true |
Whether to merge with existing content or overwrite |
Format Specifications¶
Dotenv Format¶
Standard environment variable format used by many frameworks.
Configuration:
Output (.env):
Features:
- Simple key=value pairs
- Comments supported (lines starting with #)
- Automatic quoting for values with spaces or special characters
- Preserves existing variables when merge: true
Example with multiple secrets:
secrets:
- name: DB_HOST
kind: static
config:
value: localhost
targets:
- provider: local
kind: file
config:
path: .env
format: dotenv
- name: DB_PASSWORD
kind: random_password
config:
length: 24
targets:
- provider: local
kind: file
config:
path: .env
format: dotenv
merge: true # Merge with DB_HOST
JSON Format¶
Structured format for programmatic access.
Configuration:
Output (secrets.json):
{
"database_password": "supersecret123",
"api_key": "abcdef123456",
"jwt_secret": "random-jwt-token-here"
}
Features: - Structured key-value storage - Easy to parse in any language - Pretty-printed with 2-space indentation - Supports merging with existing JSON objects
Example with nested structure:
secrets:
- name: app_config
kind: templates.application_config
templates:
application_config:
fields:
database:
generator:
kind: static
config:
default: '{"host": "localhost", "port": 5432}'
api_key:
generator:
kind: random_string
config:
length: 32
targets:
- provider: local
kind: file
config:
path: config.json
format: json
YAML Format¶
Human-readable format popular in configuration files.
Configuration:
Output (secrets.yaml):
Features: - Clean, readable format - Preserves key order - No flow style (uses block style) - Merges with existing YAML structures
Example configuration:
secrets:
- name: database_url
kind: static
config:
value: postgresql://user:pass@localhost/db
targets:
- provider: local
kind: file
config:
path: config.yaml
format: yaml
- name: redis_url
kind: static
config:
value: redis://localhost:6379
targets:
- provider: local
kind: file
config:
path: config.yaml
format: yaml
merge: true
TOML Format¶
Configuration format used by Rust, Python, and other ecosystems.
Configuration:
Output (secrets.toml):
Features: - Clear, unambiguous format - Type-safe - Good for nested configurations - Popular in Rust and Python projects
Dependencies: TOML support requires additional packages:
Complete Examples¶
Development Environment Setup¶
Create a complete .env file for local development:
version: '1.0'
secrets:
# Database configuration
- name: DB_HOST
kind: static
config:
value: localhost
targets:
- provider: local
kind: file
config:
path: .env
format: dotenv
- name: DB_PORT
kind: static
config:
value: "5432"
targets:
- provider: local
kind: file
config:
path: .env
format: dotenv
merge: true
- name: DB_NAME
kind: static
config:
value: myapp_dev
targets:
- provider: local
kind: file
config:
path: .env
format: dotenv
merge: true
- name: DB_PASSWORD
kind: random_password
config:
length: 24
special: false
targets:
- provider: local
kind: file
config:
path: .env
format: dotenv
merge: true
# Application secrets
- name: SECRET_KEY
kind: random_string
config:
length: 64
charset: base64
targets:
- provider: local
kind: file
config:
path: .env
format: dotenv
merge: true
- name: JWT_SECRET
kind: random_string
config:
length: 32
charset: hex
targets:
- provider: local
kind: file
config:
path: .env
format: dotenv
merge: true
# External API keys
- name: STRIPE_API_KEY
kind: random_string
config:
length: 32
charset: alphanumeric
targets:
- provider: local
kind: file
config:
path: .env
format: dotenv
merge: true
Result (.env):
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp_dev
DB_PASSWORD=randompassword123456789012
SECRET_KEY=base64encodedsecretkey...
JWT_SECRET=abcdef1234567890abcdef1234567890
STRIPE_API_KEY=sk_test_abc123def456ghi789jkl
Multi-Format Configuration¶
Store secrets in multiple formats simultaneously:
version: '1.0'
secrets:
- name: api_credentials
kind: templates.api_config
templates:
api_config:
description: API configuration
fields:
api_key:
generator:
kind: random_string
config:
length: 32
charset: alphanumeric
api_secret:
generator:
kind: random_string
config:
length: 64
charset: base64
endpoint:
generator:
kind: static
config:
default: https://api.example.com
targets:
# Dotenv for Docker Compose
- provider: local
kind: file
config:
path: .env
format: dotenv
# JSON for Node.js apps
- provider: local
kind: file
config:
path: config/secrets.json
format: json
# YAML for Python apps
- provider: local
kind: file
config:
path: config/secrets.yaml
format: yaml
Docker Container Secrets¶
Generate secrets for Docker containers:
version: '1.0'
secrets:
- name: POSTGRES_PASSWORD
kind: random_password
config:
length: 32
special: false
targets:
- provider: local
kind: file
config:
path: docker/.env
format: dotenv
- name: REDIS_PASSWORD
kind: random_password
config:
length: 24
special: false
targets:
- provider: local
kind: file
config:
path: docker/.env
format: dotenv
merge: true
- name: APP_SECRET_KEY
kind: random_string
config:
length: 64
charset: base64
targets:
- provider: local
kind: file
config:
path: docker/.env
format: dotenv
merge: true
Use with Docker Compose:
# docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:15
env_file:
- docker/.env
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
redis:
image: redis:7
command: redis-server --requirepass ${REDIS_PASSWORD}
env_file:
- docker/.env
app:
build: .
env_file:
- docker/.env
environment:
- SECRET_KEY=${APP_SECRET_KEY}
Best Practices¶
1. Use .gitignore¶
Always exclude secret files from version control:
# Secret files
.env
.env.local
.env.*.local
secrets.json
secrets.yaml
secrets.toml
config/secrets.*
# Keep templates
!.env.example
2. Provide Example Files¶
Create example files with dummy values:
3. Use Merge Mode Carefully¶
When merge: true:
- Existing keys are preserved
- New secrets are added
- Updated secrets overwrite old values
When merge: false:
- File is completely overwritten
- Only current secrets are included
- Previous content is lost
# Safe for multiple secrets in same file
targets:
- provider: local
kind: file
config:
path: .env
format: dotenv
merge: true # Add to existing file
# Use for single-secret files
targets:
- provider: local
kind: file
config:
path: secrets/api_key.txt
format: dotenv
merge: false # Overwrite completely
4. Organize by Environment¶
Structure files by environment:
variables:
environment: dev
secrets:
- name: database_password
kind: random_password
config:
length: 32
targets:
- provider: local
kind: file
config:
path: .env.{{var.environment}}
format: dotenv
Results in: .env.dev, .env.staging, .env.prod
5. Set Appropriate Permissions¶
Protect secret files with proper permissions:
# Restrict access to owner only
chmod 600 .env
chmod 600 secrets.json
# Or use SecretZero to set permissions
secretzero sync -f Secretfile.yml --file-mode 0600
6. Use Separate Files for Different Purposes¶
Organize secrets logically:
secrets:
# Database secrets → database.env
- name: DB_PASSWORD
targets:
- provider: local
kind: file
config:
path: secrets/database.env
format: dotenv
# API keys → api.env
- name: API_KEY
targets:
- provider: local
kind: file
config:
path: secrets/api.env
format: dotenv
# Application config → app.json
- name: app_config
targets:
- provider: local
kind: file
config:
path: config/app.json
format: json
Troubleshooting¶
File Not Created¶
Problem: Secret file doesn't exist after running sync.
Solutions: 1. Check parent directory exists (SecretZero creates missing directories) 2. Verify path is correct (relative to working directory) 3. Check file permissions on parent directory 4. Review logs for error messages
Merge Not Working¶
Problem: New secrets overwrite existing ones.
Solutions:
1. Ensure merge: true is set
2. Check file format matches existing file
3. Verify file is readable
4. Use different files if formats conflict
Format Not Supported¶
Problem: Error about unsupported format.
Solutions: 1. For TOML: Install dependencies
2. Check format spelling:dotenv, json, yaml, toml (lowercase)
3. Verify file extension matches format
Special Characters in Dotenv¶
Problem: Values with spaces or special characters break parsing.
Solution: SecretZero automatically quotes values with special characters:
# Automatically quoted
PASSWORD="p@ss w0rd!"
API_KEY="key-with-dashes"
# No quotes needed
SIMPLE_KEY=abc123
File Encoding Issues¶
Problem: Non-ASCII characters cause errors.
Solution: All files are written in UTF-8. Ensure your editor uses UTF-8:
secrets:
- name: WELCOME_MESSAGE
kind: static
config:
value: "Welcome! 你好! مرحبا!"
targets:
- provider: local
kind: file
config:
path: .env
format: dotenv
Integration Examples¶
Python Applications¶
# Load secrets from .env
from dotenv import load_dotenv
import os
load_dotenv()
database_password = os.getenv('DATABASE_PASSWORD')
api_key = os.getenv('API_KEY')
Node.js Applications¶
// Load secrets from .env
require('dotenv').config();
const databasePassword = process.env.DATABASE_PASSWORD;
const apiKey = process.env.API_KEY;
Go Applications¶
// Load secrets from .env
import "github.com/joho/godotenv"
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
databasePassword := os.Getenv("DATABASE_PASSWORD")
apiKey := os.Getenv("API_KEY")
Ruby Applications¶
# Load secrets from .env
require 'dotenv/load'
database_password = ENV['DATABASE_PASSWORD']
api_key = ENV['API_KEY']
Security Considerations¶
File Permissions¶
Local files are only as secure as their permissions:
# Secure file permissions
chmod 600 .env # Owner read/write only
chmod 700 secrets/ # Owner access to directory
# Check permissions
ls -la .env
# Should show: -rw------- (600)
Encryption at Rest¶
For sensitive data, consider encrypting files:
# Encrypt with GPG
gpg --symmetric --cipher-algo AES256 secrets.json
# Decrypt when needed
gpg --decrypt secrets.json.gpg > secrets.json
Temporary Files¶
Clean up temporary secret files:
# Generate temporary secrets
secrets:
- name: temp_token
kind: random_string
config:
length: 32
targets:
- provider: local
kind: file
config:
path: /tmp/temp_secret.txt
format: dotenv
Then clean up:
Backup Considerations¶
When backing up systems: - Exclude secret files from backups - Or use encrypted backups - Rotate secrets after backup restoration
Next Steps¶
- Explore AWS targets for cloud storage
- Learn about Kubernetes secrets for containers
- Review CI/CD targets for automation
- Check examples for more patterns