dagger-env
A type-safe, reusable environment configuration abstraction for Dagger modules with full Zod v4 validation and 1Password integration.
Features
- 🔒 Type-safe: Full TypeScript support with Zod v4 validation
- 🔄 Reusable: Create multiple environment configurations for different projects
- 🎯 Consistent: Standardized API across all Dagger modules
- 🛡️ Validated: Runtime validation of arguments, environment variables, and secrets
- 📦 Modular: Secret presets and derived environment variables
- 🔐 1Password Integration: Built-in command runner with
op run support
- 🚀 Easy to use: Simple configuration-based setup
Installation
npm install dagger-env zod
Note: The command runner functionality (dagger-env/run) requires the 1Password CLI (op) to be installed.
Quick Start
import { createDaggerEnv } from 'dagger-env'
import { z } from 'zod/v4'
const myDaggerEnv = createDaggerEnv({
args: z.object({
push: z.string().optional(),
environment: z.enum(['dev', 'staging', 'prod']).optional()
}),
env: z.object({
CI: z.string().optional(),
NODE_ENV: z.string().optional()
}),
secrets: z.object({
API_TOKEN: z.string(),
DATABASE_URL: z.string(),
REDIS_URL: z.string()
}),
secretPresets: {
api: ['API_TOKEN', 'DATABASE_URL'],
cache: ['REDIS_URL']
},
derivedEnvVars: {
API_TOKEN: {
API_BASE_URL: 'https://api.example.com',
API_VERSION: 'v1'
},
DATABASE_URL: {
DB_POOL_SIZE: '10'
}
}
})
import { Container, dag, func, object, Secret } from '@dagger.io/dagger'
@object()
export class MyModule {
@func()
async build(options: Secret): Promise<Container> {
const opts = await myDaggerEnv.parseDaggerOptions(options)
const { withEnv } = await myDaggerEnv.getWithEnv(options, ['api'], ['REDIS_URL'])
return withEnv(dag.container().from('node:18')).withExec(['npm', 'run', 'build']).sync()
}
}
Command Runner (1Password Integration)
For projects using 1Password for secret management, dagger-env provides a convenient command runner that integrates with op run:
import { createDaggerEnv } from 'dagger-env'
import { createDaggerCommandRunner } from 'dagger-env/run'
import { z } from 'zod/v4'
const myDaggerEnv = createDaggerEnv({
args: z.object({
environment: z.enum(['dev', 'staging', 'prod']).optional()
}),
env: z.object({
CI: z.string().optional(),
NODE_ENV: z.string().optional()
}),
secrets: z.object({
API_TOKEN: z.string()
}),
secretPresets: {
api: ['API_TOKEN']
},
derivedEnvVars: {}
})
const runDaggerCommand = createDaggerCommandRunner({
opVault: 'your-vault-id',
opItem: 'your-item-id',
opSections: [
{
id: 'your-section-id',
label: 'Shared'
}
],
dockerCommands: ['build', 'deploy', 'test'],
daggerEnv: myDaggerEnv
})
await runDaggerCommand('test', {
args: { environment: 'dev' },
env: { NODE_ENV: 'development' }
})
Advanced Configuration
const runDaggerCommand = createDaggerCommandRunner({
opVault: 'your-vault-id',
opItem: 'your-item-id',
opSections: [
{ id: 'shared-section-id', label: 'Shared' },
{ id: 'project-section-id', label: 'Project Specific' }
],
dockerCommands: ['build', 'deploy', 'test'],
beforeCommand: async () => {
console.log('Setting up environment...')
},
daggerEnv: myDaggerEnv
})
API Reference
Environment Configuration
createDaggerEnv(config)
Creates a new DaggerEnv instance with the provided configuration.
Parameters:
config.args: Zod schema for command-line arguments
config.env: Zod schema for environment variables
config.secrets: Zod schema for secrets
config.secretPresets: Object mapping preset names to arrays of secret names
config.derivedEnvVars: Object mapping secret names to derived environment variables
Returns: DaggerEnv<T> instance
daggerEnv.parseDaggerOptions(options: Secret)
Parses and validates dagger options from a Secret containing JSON.
Parameters:
options: Dagger Secret containing JSON options
Returns: Promise<DaggerOptionsFromConfig<T>> - Parsed and typed options object
daggerEnv.getWithEnv(options, secretPresets, secretNames?)
Creates a function that applies environment variables and secrets to a container.
Parameters:
options: Secret or parsed options object
secretPresets: Array of preset names to include (e.g., ['api', 'cache'])
secretNames: Optional array of additional individual secret names
Returns: Promise<(con: Container) => Container> - Function that applies env vars and secrets
daggerEnv.getOptionsSchema()
Returns the Zod schema for the complete options object. Primarily used internally by the command runner, but available for advanced use cases.
Returns: ZodObject - The combined schema for args, env, and secrets
daggerEnv.getSecretPresets()
Returns array of available secret preset names.
Returns: Array<string> - Available preset names
daggerEnv.getPresetSecrets(preset)
Returns array of secret names for a specific preset.
Parameters:
preset: Name of the preset
Returns: readonly string[] - Secret names in the preset
Command Runner
createDaggerCommandRunner(config)
Creates a function to run Dagger commands with 1Password integration.
Parameters:
config.opVault: 1Password vault ID
config.opItem: 1Password item ID
config.opSections: Array of 1Password sections to include for secrets
config.dockerCommands: Optional array of command names that should include Docker socket
config.beforeCommand: Optional async function to run before executing the command
config.daggerEnv: DaggerEnv instance for schema validation and type safety
Returns: (commandName: string, options?: RunDaggerCommandOptions) => Promise<void> - Function to execute Dagger commands
RunDaggerCommandOptions
Options for individual command execution:
args: Optional record of arguments to pass to the Dagger command
env: Optional record of additional environment variables
extraArgs: Optional array of additional command-line arguments
Configuration Examples
Simple API Service
const apiServiceEnv = createDaggerEnv({
args: z.object({
push: z.string().optional()
}),
env: z.object({
CI: z.string().optional()
}),
secrets: z.object({
API_TOKEN: z.string(),
DATABASE_URL: z.string()
}),
secretPresets: {
api: ['API_TOKEN', 'DATABASE_URL']
} as const,
derivedEnvVars: {
API_TOKEN: {
API_BASE_URL: 'https://api.example.com'
}
} as const
})
Multi-Environment Setup
const multiEnvDaggerEnv = createDaggerEnv({
args: z.object({
environment: z.enum(['dev', 'staging', 'prod']),
push: z.string().optional()
}),
env: z.object({
CI: z.string().optional()
}),
secrets: z.object({
DEV_API_KEY: z.string(),
STAGING_API_KEY: z.string(),
PROD_API_KEY: z.string()
}),
secretPresets: {
dev: ['DEV_API_KEY'],
staging: ['STAGING_API_KEY'],
prod: ['PROD_API_KEY']
} as const,
derivedEnvVars: {
DEV_API_KEY: { API_URL: 'https://dev-api.example.com' },
STAGING_API_KEY: { API_URL: 'https://staging-api.example.com' },
PROD_API_KEY: { API_URL: 'https://api.example.com' }
} as const
})
For advanced use cases where you need to extract TypeScript types:
import { z } from 'zod/v4'
import type { DaggerOptionsFromConfig } from 'dagger-env'
type MyDaggerEnvConfig = typeof myDaggerEnv extends DaggerEnv<infer T> ? T : never
type MyOptions = DaggerOptionsFromConfig<MyDaggerEnvConfig>
const schema = myDaggerEnv.getOptionsSchema()
type SchemaOutput = z.output<typeof schema>
Best Practices
- Use
as const for secretPresets and derivedEnvVars to ensure proper typing
- Group related secrets into logical presets (e.g.,
api, database, cache)
- Validate early by calling
parseDaggerOptions() at the start of functions
- Reuse configurations across multiple Dagger modules in the same project
- Document your schemas with JSDoc comments for better developer experience
Requirements
- Node.js 18+
- Dagger SDK
- Zod v4+
License
MIT
Contributing
Contributions are welcome! Please read our contributing guidelines and submit pull requests to our GitHub repository.