Protocol v2.0.0 introduces release-based deployment as the recommended strategy. This guide walks you through migrating from the legacy branch-based approach.
| Branch-Based (legacy) | Release-Based (v2.0.0+) | |
|---|---|---|
| Deploy unit | Branch tip (latest commit) | Git tag (semver) |
| How nodes update | Poll branch, git pull |
Poll GitHub variable, git checkout tag |
| Rollback | Revert commit + push | deploy:rollback (instant) |
| Audit trail | Git log only | Structured audit log (~/.protocol/.node/deployments.log) |
| Version tracking | None | VERSION file, NodeConfig, GitHub Releases |
| Multi-node deploy | Push to branch | Set one variable, all nodes update |
The fastest way to migrate is the interactive wizard:
protocol migrateThis walks you through every step and handles edge cases. The rest of this guide explains what happens under the hood.
protocol self:updateVerify you're on v2.0.0+:
protocol --versionUpdate your protocol.json:
{
"deployment": {
"strategy": "release",
"pointer": "github_variable",
"pointer_name": "PROTOCOL_ACTIVE_RELEASE"
}
}Or use the init command to update interactively:
protocol init
# Select "Change deployment strategy" → "Release-based"Tag the current state of your code as the first release:
protocol release:create 1.0.0This will:
- Write
1.0.0to theVERSIONfile - Commit the change
- Create a git tag
1.0.0 - Push to remote
- Create a GitHub Release
Point all nodes to your first release:
protocol deploy:push 1.0.0This sets the PROTOCOL_ACTIVE_RELEASE GitHub repository variable. Any running nodes will detect the change and deploy automatically.
If you have .env files in your config repo, you can encrypt them:
# Generate encryption key (do this on ONE machine)
protocol secrets:setup
# Copy the displayed key to other nodes:
# protocol secrets:setup "your-hex-key-here"
# Encrypt your .env file
protocol secrets:encrypt
# Remove the plaintext .env from the config repo
rm ../myapp-config/.env
# Commit the encrypted version
protocol config:saveUpdate protocol.json to use encrypted secrets:
{
"deployment": {
"secrets": "encrypted"
}
}On each node, restart to pick up the new strategy:
protocol stop
protocol startThe node will now run deploy:slave (release watcher) instead of git:slave (branch watcher).
After migration, check that everything is working:
# Check node status
protocol status
# Verify the active release
protocol deploy:status
# View the audit log
protocol deploy:logThe status output should show:
- Deployment Strategy:
release - Current Release: Your version tag
- Release Watcher: Running with a PID
The deployment workflow changes from push-to-branch to tag-and-deploy:
git push origin master
# Nodes auto-pull within seconds# 1. Create a release (tags + pushes + GitHub Release)
protocol release:create
# 2. Test on staging (optional)
protocol node:deploy 1.2.3 # On staging node only
# 3. Deploy to all nodes
protocol deploy:push 1.2.3
# 4. Something wrong? Instant rollback
protocol deploy:rollbackRollback is now instant and doesn't require reverting commits:
# Roll back ALL nodes
protocol deploy:rollback
# Roll back a single node (e.g. staging)
protocol node:rollbackThe previous version is tracked in NodeConfig (release.previous), so rollback is always one command away.
If you prefer the legacy approach, you don't have to migrate. Branch-based mode continues to work in v2.0.0. Just make sure your protocol.json has:
{
"deployment": {
"strategy": "branch"
}
}Or simply omit the deployment.strategy key — it defaults to branch.
Release-based deployment requires the gh CLI for managing repository variables. Install it:
# macOS
brew install gh
# Ubuntu/Debian
sudo apt install gh
# Then authenticate
gh auth loginMake sure gh is authenticated with access to your repository:
gh auth status
gh repo view- Check the release watcher is running:
protocol status - Check the active release:
protocol deploy:status - Check the audit log for errors:
protocol deploy:log - Restart the watcher:
protocol stop && protocol start
Protocol no longer uses protocol.lock for runtime state. All state is now stored in two locations:
- NodeConfig (
~/.protocol/.node/nodes/<project>.json) — release tracking, bluegreen settings, and shared configuration - Per-release deployment.json (
<release_dir>/<version>/.protocol/deployment.json) — per-release runtime state (port, status, container name, watcher PID)
No manual action is required. When you run protocol start, Protocol automatically detects any existing protocol.lock file and migrates its state to NodeConfig and deployment.json. The migration happens once on first run.
After migration, the protocol.lock file is no longer read or written. You can safely delete it.
Old Location (protocol.lock) |
New Location |
|---|---|
release.current |
NodeConfig: release.active |
release.previous |
NodeConfig: release.previous |
release.deployed_at |
Per-release: .protocol/deployment.json |
release.slave.pid |
Per-release: .protocol/deployment.json (watcher_pid) |
bluegreen.active_version |
NodeConfig: release.active |
bluegreen.previous_version |
NodeConfig: release.previous |
bluegreen.shadow_version |
NodeConfig: bluegreen.shadow_version |
bluegreen.promoted_at |
NodeConfig: bluegreen.promoted_at |
bluegreen.releases.<version>.port |
Per-release: .protocol/deployment.json (port) |
bluegreen.releases.<version>.status |
Per-release: .protocol/deployment.json (status) |
.env.bluegreenhas been renamed to.env.deployment. Protocol handles this rename automatically during migration.- Release directory detection now uses path comparison against
release.releases_dirin NodeConfig, rather than checking for the existence of specific files. - The
release.*namespace in NodeConfig holds shared config used by both release and bluegreen strategies:releases_dir,git_remote,active,previous,target,versions. - The
bluegreen.*namespace holds only bluegreen-specific config:shadow_version,auto_promote,health_checks,promoted_at.
~/.protocol/.node/nodes/<project>.json ← all runtime state (replaces protocol.lock)
<releases_dir>/<version>/.protocol/
└── deployment.json ← per-release state (port, status, container_name)
<releases_dir>/<version>/.env.deployment ← port config (renamed from .env.bluegreen)