Skip to main content

Migrating from v6 to v7

TL;DR​

  • When upgrading to v7 - you will have to run exoframe update twice - first to update server to latest version, then to let server upgrade Traefik to v3 (Exoframe v6 only upgraded to latest v2).
    • The Exoframe server itself can be upgraded either with exoframe upgrade or by running the new install.sh script (make sure to pull traefik and exoframe-server images before running the script).
  • Exoframe CLI now requires deploy subcommand to be explicitly passed. So, instead of exoframe [-u] [folder] you have to do exoframe deploy [-u] [folder]
  • Config for both Exoframe CLI and Exoframe Server is now located under $XDG_CONFIG_HOME/exoframe instead of $HOME/.exoframe
    • Server upgrade handles that automatically since it's a mount
    • For cli upgrade you need to run mv ~/.exoframe ~/.config/exoframe to keep your current config
  • Traefik update to v3
  • All deps updated to latest versions (Exoframe v7 also works on Node 25 and Docker 29)

Breaking changes at a glance​

  • Node 18+ required: v7 switched the CLI/server codebase to native ESM. Make sure your local Node version (and the server host) runs Node 18 or newer before updating.
  • Yarn removed: all packages now use npm. Delete yarn.lock-only workflows or replace them with npm/pnpm equivalents.
  • Plugins removed: the plugin system is gone. If you depended on a plugin, consider replicating the behavior via templates or custom recipes.
  • Swarm removed: Docker Swarm deployments and related CLI flags are no longer available. Exoframe now targets single-host Docker setups only.
  • Docker Compose removed: compose files are no longer accepted as deployment input - see the section below for the recommended alternative.
  • Exoframe FaaS removed: the function-as-a-service subsystem (exoframe-faas) has been discontinued.
  • Config folder moved: CLI and server configs now follow XDG_CONFIG_HOME (falls back to ~/.config/exoframe). Update any scripts or mounts that pointed to the old location.

Why docker-compose support was removed​

Exoframe v6 treated an entire docker-compose.yml file as one deployment. Every push forced the server to tear down and recreate all services in the compose file - even the ones that did not change, like databases, queues, or caches. That means longer deploy times, unnecessary downtime, and extra resource usage just to redeploy a single app container.

With v7 we lean into the "Exoframe way": describe each long-lived service in its own Exoframe config once, then redeploy only the service that changed. You keep the fast iterative workflow of Exoframe without repeatedly restarting stateful dependencies.

Example migration​

Common local docker-compose setup:

docker-compose.yml
version: '3.6'
services:
postgres:
image: postgres:15
ports:
- 5432:5432
volumes:
- db_data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: postgrespassword
redis:
image: redis:7
ports:
- '6379:6379'
app:
build: .
ports:
- 8080:8080
depends_on:
- postgres
- redis
environment:
DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres
REDIS_HOST: redis

volumes:
db_data:

Deploying that file with Exoframe v6 restarts postgres and redis on every deployment even if only the app changes. Instead, define each service once:

postgres/exoframe.json
{
"name": "my-postgres",
"domain": false,
"image": "postgres:15",
"volumes": ["db_data:/var/lib/postgresql/data"],
"hostname": "mypostgres",
"env": {
"POSTGRES_PASSWORD": "postgrespassword"
}
}
redis/exoframe.json
{
"name": "my-redis",
"domain": false,
"image": "redis:7",
"hostname": "myredis"
}
app/exoframe.json
{
"name": "my-app",
"domain": "my-app.com",
"env": {
"DATABASE_URL": "postgres://postgres:postgrespassword@postgres:5432/postgres",
"REDIS_HOST": "myredis"
}
}

Now redeploy the app without touching the database or cache, and only redeploy those infrastructure services when they change. The setup takes a few extra minutes once, and every deploy after that is simpler, faster, and safer.

We are exploring helper tooling that can auto-convert compose files into Exoframe configs, but the recommended path today is to define each service explicitly as shown above.

Deployment UUIDs persist across updates​

Exoframe v7 keeps the same deployment UUID when you redeploy a project using exoframe deploy --update. The container name, EXOFRAME_DEPLOYMENT env var, and Traefik router IDs no longer get regenerated on every update, so CLI commands like exoframe logs <id> and exoframe rm <id> keep working across upgrades. This behavior also means that any automatically generated domain that comes from baseDomain stays identical after an update (<project-name>.<baseDomain>), eliminating the need to refresh DNS records or cached webhooks whenever you ship a new version.