xenvsync

Migration Playbook: From dotenv / git-crypt / sops to xenvsync

Published March 27, 2026 · 10 min read · Migration Guide

Before You Start

Migrating secret management is high-stakes work. The goal of this playbook is to make each phase independently reversible. You should be able to complete Phase 1 and stop, run both systems in parallel during Phase 2, and only cut over fully in Phase 4 once you have validated that xenvsync works end-to-end in your environment.

Estimated timeline: One sprint (one week) for most teams. The bottleneck is usually CI validation and getting all team members to generate identities — not the technical setup.

From plaintext .env

1–2 days

Fastest path — nothing to replace, just add encryption.

From dotenv-vault

2–3 days

Need to export secrets from service and re-encrypt locally.

From git-crypt / sops

3–5 days

Decrypt existing vault, re-encrypt with xenvsync, validate CI.

Phase 1: Inventory and Install

Before touching any secrets, understand what you have. List every .env file, vault artifact, and CI secret in use. This inventory becomes your test checklist in Phase 4.

Inventory your current statebash
# Find all .env files in the repo (excluding node_modules)
$ find . -name ".env*" -not -path "*/node_modules/*" -not -path "*/.git/*"

# List all CI secrets (check your provider's UI or CLI)
# GitHub: gh secret list
# GitLab: glab variable list

# Note: environments in use
$ ls .env* | grep -v ".vault" | grep -v ".example"
Install xenvsyncbash
# npm (all platforms)
$ npm install -g @nasimstg/xenvsync

# macOS / Linux (Homebrew)
$ brew install nasimstg/tap/xenvsync

# Windows (Scoop)
$ scoop install xenvsync

# Verify
$ xenvsync version

Phase 2: Encrypt One Non-Production Environment

Start with staging or dev — never production first. This lets you validate the full push/pull/run cycle with low risk. Run the old and new systems in parallel until you are confident.

Phase 2 — first encryptionbash
# Initialize in the project root
$ xenvsync init
# ✓ Generated .xenvsync.key (mode 0600)
# ✓ Updated .gitignore

# Encrypt the staging environment
$ xenvsync push --env staging

# Validate: decrypt and check contents
$ xenvsync pull --env staging
$ diff .env.staging .env.staging.backup   # compare to known-good

# Verify vault integrity
$ xenvsync verify --env staging
$ xenvsync doctor

# Commit the vault
$ git add .env.staging.vault
$ git commit -m "migrate: add xenvsync encrypted staging vault"
Important: Keep your old secret management running in parallel. Do not delete dotenv-vault files, git-crypt keys, or sops configs until Phase 4 validation is complete.

Phase 3: Transition CI to Runtime Key Injection

Update one CI job to use xenvsync. Store the key value as a CI secret and write it to a file at runtime. Validate that the job passes before updating all jobs.

GitHub Actions — updated jobyaml
# Add XENVSYNC_STAGING_KEY to repo secrets
# Settings → Secrets → Actions → New repository secret

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

      # New: inject xenvsync key
      - name: Inject key
        run: |
          echo "${{ secrets.XENVSYNC_STAGING_KEY }}" > .xenvsync.key
          chmod 600 .xenvsync.key

      # Remove: old env var injection or dotenv-vault step
      # - run: dotenv-vault pull ...

      - name: Run tests
        run: xenvsync run --env staging -- npm test
GitLab CI — updated scriptbash
test:
  variables:
    # Add XENVSYNC_STAGING_KEY as a masked CI/CD variable
  script:
    - echo "$XENVSYNC_STAGING_KEY" > .xenvsync.key && chmod 600 .xenvsync.key
    - xenvsync run --env staging -- npm test

Phase 3b: Set Up Team Keys (if using team sharing)

If you are moving away from a shared-key model, have each team member generate their X25519 identity now. Collect all public keys before re-pushing the vault.

Team migration to V2 vaultsbash
# Each team member (once per machine)
$ xenvsync keygen
$ xenvsync whoami   # share this output with the maintainer

# Maintainer: build roster from collected public keys
$ xenvsync team add alice   <alice-pubkey>
$ xenvsync team add bob     <bob-pubkey>
# ... add all members

# Re-push as V2 vault
$ xenvsync push --env staging
$ xenvsync push --env production
$ git add .xenvsync-team.json *.vault
$ git commit -m "migrate: switch to V2 per-member vaults"

Phase 4: Validate and Cut Over

Run this checklist before removing the old system:

xenvsync pull works for every team member on their machine.
xenvsync run works in local dev for all services.
CI passes on all pipelines using xenvsync key injection.
xenvsync doctor reports no failures or security warnings.
xenvsync verify passes on all vaults.
All team members have generated identities and pulled successfully (for V2).
Pre-commit hook installed and tested — blocks plaintext .env staging.
Final cut-over commandsbash
# Encrypt production environment
$ xenvsync push --env production

# Run full audit
$ xenvsync doctor
$ xenvsync verify --env staging
$ xenvsync verify --env production

# Commit everything
$ git add .
$ git commit -m "migrate: complete xenvsync migration across all environments"

# Now safe to remove old artifacts:
# - .env.vault (dotenv-vault format if different)
# - .git-crypt keys
# - sops .decrypted files
# - old CI secret variables

Rollback Plan

Because each phase is additive (you never delete the old system until Phase 4), rollback at any phase is straightforward:

  • Phase 1 rollback: Nothing changed — just delete .xenvsync.key and the vault files.
  • Phase 2 rollback: Revert the vault commit. The old system is still functional.
  • Phase 3 rollback: Revert the CI job change. Old secrets variables are still there.
  • Phase 4 rollback: If you did not delete old artifacts, revert the commit and restore the old CI variables.
Note: xenvsync vaults are format-stable and forward-compatible. V1 and V2 vaults are both supported in all current and future xenvsync versions. You will not be locked in to a specific version after migration.

Migration from Specific Tools

From plaintext .env files

Fastest path. Just run xenvsync init and xenvsync push. The .gitignore update happens automatically. Run Phase 2–4 in a single day.

From dotenv-vault

Export your decrypted secrets from the dotenv-vault service into a local .env file. Then run xenvsync init and push. The key difference: xenvsync never calls an external service — the vault is self-contained in your repo.

From git-crypt

Run git-crypt unlock to decrypt the repo, copy the plaintext secrets to a temporary .env file, then run xenvsync init and push. After validating, remove the git-crypt filter configuration from .gitattributes.

From sops

Run sops --decrypt secrets.yaml to extract plaintext values. Convert to KEY=VALUE format in a .env file. Then follow the standard xenvsync init and push flow. For teams already using age keys, note that X25519 key derivation in xenvsync is independent of age key format.