Ghostable's validation keeps bad environment variables out of your deployments without ever uploading plaintext values or schema details. In this tutorial you'll define validation rules, run them locally, wire them into CI, and learn patterns that keep Laravel, Node/Next.js, Rails, and Django apps from shipping broken config.
What you'll build
- A project-level schema in
.ghostable/schema.yamlwith sensible defaults. - Environment overrides (e.g., production-only rules) in
.ghostable/schemas/<env>.yaml. - A repeatable command:
ghostable env validate --env <env>that fails fast when config drifts. - A CI job that blocks merges/deploys when validation fails.
Prerequisites
- Ghostable CLI installed and authenticated (
ghostable login --token $GHOSTABLE_TOKEN). - A project initialized with
.ghostable/ghostable.yamlso the CLI knows which project/environment to target. - Local
.envfiles for the environments you want to validate. - Node.js available if you're running the CLI in CI (see GitHub Actions example below).
1. Define your validation schema
All validation lives locally inside .ghostable (see the validation docs). Start with a global schema file and add per-environment overrides only when needed.
.ghostable/
ghostable.yaml
schema.yaml # global rules
schemas/
production.yaml # env-specific overrides
staging.yaml
Create .ghostable/schema.yaml with baseline rules that apply everywhere:
APP_NAME:
- required
- string
- max:64
APP_ENV:
- required
- in:local,staging,production
APP_DEBUG:
- required
- boolean
APP_URL:
- required
- url
LOG_CHANNEL:
- required
- in:stack,stdout
Add overrides for stricter environments (e.g., production):
# .ghostable/schemas/production.yaml
APP_DEBUG:
- required
- boolean
- in:false
APP_KEY:
- required
- starts_with:base64:
- min:44
QUEUE_CONNECTION:
- required
- in:redis,sqs
Ghostable merges the global schema with the matching override file before validating your .env.
2. Add rules for your framework
Here are practical rule sets you can drop into schema.yaml. Tweak names and allowed values to match your stack.
Laravel
APP_KEY:
- required
- starts_with:base64:
- min:44
SESSION_DRIVER:
- required
- in:file,database,redis
CACHE_DRIVER:
- required
- in:file,redis,memcached,dynamodb
MAIL_MAILER:
- required
- in:smtp,log,mailgun,postmark,sendmail
DB_CONNECTION:
- required
- in:mysql,pgsql,sqlsrv,sqlite
Next.js / Node
NODE_ENV:
- required
- in:development,staging,production
NEXT_PUBLIC_API_URL:
- required
- url
DATABASE_URL:
- required
- regex:^postgres://
JWT_SECRET:
- required
- min:32
- starts_with:sk_
Django / Rails
SECRET_KEY:
- required
- min:50
DJANGO_SETTINGS_MODULE:
- required
- ends_with:.settings
RAILS_ENV:
- required
- in:development,test,production
DATABASE_URL:
- required
- regex:^postgres://
ALLOWED_HOSTS:
- required
- string
Prefer in: for controlled enums, starts_with/regex for keys and URLs, and required everywhere you expect the app to boot.
3. Validate locally before merging
Run validation against any environment. The CLI loads schema.yaml, merges the matching override, and compares against your resolved .env (you can pass --file for non-standard names).
ghostable env validate --env production
# or validate a custom file:
ghostable env validate --env staging --file .env.staging
Failures are human-readable and the command exits non-zero:
X APP_KEY must start with "base64:"
X APP_DEBUG must be "false"
! QUEUE_CONNECTION is set to sync (recommended: redis)
Fix values locally, rerun, and only push or deploy once validation passes.
4. Gate deploys in CI/CD
Because validation happens locally (zero-knowledge), it fits neatly into your pipeline. Here is a GitHub Actions job that blocks merges when validation fails:
name: Validate env
on:
pull_request:
workflow_dispatch:
jobs:
validate-env:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm install @ghostable/cli@latest
- name: Validate production schema
env:
GHOSTABLE_TOKEN: ${{ secrets.GHOSTABLE_TOKEN }}
run: |
ghostable login --token "$GHOSTABLE_TOKEN"
ghostable env validate --env production
Apply the same pattern to deploy jobs so release pipelines fail fast when config drifts.
What not to do
- Don't copy real secrets into schema files or checked-in
.envexamples - keep schemas as contracts, not storage. - Don't treat warnings as optional in production. Tighten
in:lists or add overrides so prod rules are explicit. - Don't skip validation for "small" changes. A single mistyped queue driver or URL can break deploys.
- Don't maintain divergent schemas per developer. Keep one shared
.ghostablefolder in version control.
Why validation protects deploys
- Prevents broken releases: schema violations fail pipelines before code hits servers.
- Codifies expectations: rules document required keys and safe values across every environment.
- Stops config drift: pairing validation with
ghostable env push|pullkeeps remote and local envs aligned. - Safer rotations: regex and
starts_withchecks catch half-rotated keys or wrong providers.
Next steps
- Expand schemas with optional keys marked
nullableto reduce noise while staying explicit. - Add staging/preview overrides that mirror production (minus the strictest checks) to surface drift earlier.
- Continue with the validation deep-dive for full rule syntax and tips.