Upgrades & Reinstall¶
Soulacy is built so that upgrades are boring: schemas only move forward, API contracts are pinned by tests, and a broken plugin can never take the gateway down. This page covers the standard upgrade flow, what guarantees back it, and the (destructive) full-reinstall escape hatch.
The one-command upgrade¶
Whatever machine your gateway runs on, this figures out how it was deployed and does the right thing:
- Host install (binary in
~/.local/bin, launchd/systemd): self-upgrades withsy upgradeand restarts the service. - Docker / Compose: pulls the newer image (or rebuilds from source if your compose builds locally) and recreates the container.
- Source checkout:
git pull, rebuilds, restarts. - Managed platform (Railway / Render / Fly / Cloud Run / Heroku / Kubernetes): there's no shell to act in, so it prints the exact redeploy step for that platform. Run those from the platform's dashboard/CLI.
It never deletes data or touches your workspace/config, and when it can't tell, it prints your options instead of guessing.
Per-platform quick reference¶
| Deployment | How to upgrade |
|---|---|
| Host binary | sy upgrade (or auto-update every 6h), then restart the service |
| Docker/Compose (image) | docker compose pull soulacy && docker compose up -d soulacy |
| Docker/Compose (builds from source) | git pull && docker compose build soulacy && docker compose up -d soulacy |
| Railway | Dashboard → service → Redeploy (bump the pinned image tag first if you pin one) |
| Render | Dashboard → Manual Deploy → Deploy latest (or set the image tag) |
| Fly.io | fly deploy, or release a new image tag |
| Google Cloud Run | gcloud run deploy <svc> --image ghcr.io/vmodekurti/soulacy-personal:0.1 |
| Kubernetes | kubectl set image deploy/soulacy soulacy=ghcr.io/vmodekurti/soulacy-personal:0.1.NEW |
| One-click AWS / Azure (container on a VM) | Run the upgrade on the VM with a remote command: see below. Never change the stack's ImageVersion/customData on a live instance: that replaces the VM and destroys its data volume. |
Image: ghcr.io/vmodekurti/soulacy-personal: tags: a version (0.1.23), the
minor (0.1), or latest.
Upgrading a cloud VM remotely (no SSH needed)¶
The one-click AWS and Azure deployments run Soulacy as a container on a VM you own, with the data in a Docker volume on that VM. Upgrade on the same instance so the data stays: triggered from your laptop:
- AWS (the one-click instance has Systems Manager enabled):
deploy/aws/upgrade-remote.sh <instance-id> [--profile p] [--region r] - Azure (Run Command):
deploy/azure/upgrade-remote.sh <resource-group> <vm-name> - Any VM with SSH:
ssh <vm> 'curl -fsSL https://raw.githubusercontent.com/vmodekurti/soulacy-personal/main/upgrade.sh | bash'
Each runs upgrade.sh on the VM (docker compose pull && up -d in /opt/soulacy),
waits for health, and never touches the volume.
Warning: do not "upgrade" by updating the CloudFormation stack's
ImageVersion or the ARM template's customData on a running instance. Those
live in the instance's boot script, so the change replaces the VM; the root
disk (and the data volume on it) is deleted with it. ImageVersion is for
choosing the version of a new deployment only.
Identify how Soulacy was installed¶
A tagged release such as 0.1.8 can be compared with the release manifest. A
source build may identify itself by a commit such as 8c2c66f; the updater
cannot reliably determine whether that commit is newer or older than a semantic
release and reports versions are not comparable. In that case, install the
desired tagged release explicitly rather than overriding the comparison.
Automatic updates¶
A release install keeps itself current. The gateway checks the signed release
manifest on a schedule (every 6 hours by default) and, when a newer release
exists, downloads the artifact for its platform, verifies the checksum and the
Sigstore signature, replaces its own soulacy and sy binaries, runs the new
binary to confirm it starts and reports the expected version, and restarts
into it. Restarts wait until no agent run is in flight (up to idle_wait, two
hours by default). If the new binary fails verification, the previous binaries
are put back and the failure is reported.
updates:
auto: true # set false to be notified only
check_interval: 6h # minimum 15m
idle_wait: 2h # 0s restarts immediately
manifest_url: "" # blank = the official GitHub release manifest
The same controls are on Config → Version (toggle, interval) and apply
without a restart. Config and the Dashboard show what the checker will
do: install, notify, or off, with the reason. On a host that supports
in-place replacement, the update banner offers Upgrade Now. On a container
or managed deployment it offers How to upgrade and shows the detected
platform's redeploy steps and exact target image instead.
After the platform reports a healthy deployment, choose I've redeployed,
check again in that prompt. Soulacy checks the signed release manifest and
its running version again; if the old instance is still serving, the prompt
stays open and tells you which version answered.
The gateway only notifies, and never replaces binaries, when:
- it runs in a container (upgrade by pulling a newer image), or
- its install directory is not writable by the gateway user (use
sudo sy update install --yes, or fix permissions), or - the running build is not a tagged release (a source build reports "versions are not comparable").
Every check, install, rollback and restart is logged and appears in Activity
as a system.update event. GET /api/v1/system/updates/status reports the
mode, last check, last applied version, any pending restart, and structured
upgrade guidance (upgrade_strategy, deployment_platform,
upgrade_instructions, and target_image). The manual upgrade endpoint also
refuses an in-place binary replacement when the reported strategy is
instructions.
Release installation: standard upgrade¶
Use sudo only when the binaries are root-owned under /usr/local/bin. Restart
the same service that normally runs the gateway:
Root-owned binaries with config outside root's home
sudo may not load your user's or service's config.yaml. Pass the release
manifest explicitly:
# system service on a VPS
sudo systemctl restart soulacy
# user service installed by sy daemon
systemctl --user restart soulacy
Verify the version and startup logs after restarting.
Source checkout: rebuild upgrade¶
cd ~/path/to/soulacy
git pull
# Rebuild GUI + binaries
(cd gui && npm install && npm run build)
make all
# Restart the gateway (foreground example)
lsof -ti :18789 | xargs kill 2>/dev/null || true
./bin/soulacy
On macOS checkouts, build-and-restart.command wraps the rebuild +
restart steps. Under launchd/systemd, replace the last two steps with
your service manager's restart.
Do not mix a source checkout upgrade with sy update install unless you intend
to replace the source-built binaries with release binaries. Stores upgrade
their own schemas at boot; there is no separate migration command.
Source builds now require Go 1.26.10 or newer. Binary release installs do not require a local Go toolchain.
Confirm the new build is running¶
Every build of the gateway has a codename, such as lively-lynx, derived from a
hash of the running binary. It changes whenever the code changes, so after a
redeploy you only need to check that the name is different.
- Portal: the badge in the top bar shows the current codename, marked new for 15 minutes after the runtime starts. Click it for the build ID, version, commit, and start time. When the runtime changes while the portal is open, a banner names the new and previous builds; reload to load the matching interface. A restart of the same build keeps the name and is reported as a restart.
- CLI:
sy versionprints the running build and its details. - API:
GET /api/v1/healthincludes abuildobject withlabel,build_id,version,commit,started_at, andinstance_id. - Logs: the
Soulacy startingline recordsbuildandbuild_id.
Rebuilding identical source produces an identical binary, so it keeps the same codename.
Verify a systemd service uses the intended config¶
An interactive soulacy serve runs as your shell user; a system service often
runs as the dedicated soulacy user with home /var/lib/soulacy. Those two
processes do not automatically read the same config.yaml.
Inspect the service identity and environment:
sudo systemctl show soulacy -p User -p Group -p ExecStart --no-pager
sudo systemctl show soulacy -p Environment --value \
| tr ' ' '\n' \
| grep -E '^(HOME|SOULACY_CONFIG_PATH|SOULACY_WORKSPACE)='
For a system service, keep configuration in a service-readable location and make the path explicit in a systemd override:
[Service]
Environment=SOULACY_CONFIG_PATH=/etc/soulacy/config.yaml
Environment=SOULACY_WORKSPACE=/var/lib/soulacy
Then reload and verify:
sudo install -d -o soulacy -g soulacy /etc/soulacy /var/lib/soulacy
sudo chown root:soulacy /etc/soulacy/config.yaml
sudo chmod 640 /etc/soulacy/config.yaml
sudo systemctl daemon-reload
sudo systemctl restart soulacy
sudo journalctl -u soulacy -n 100 --no-pager
Do not solve this by making a credential-bearing configuration world-readable.
Agent packages: v1 deprecation timeline
If you install agent packages via sy pull / Agents → Import, note
that the legacy v1 package schema is deprecated and refused after the
2027-06-01 cutoff. v1 imports today still succeed with a warning; move
packages you own to v2 (namespaced ids like owner/name, calendar
versioning YYYY.MM.DD, and typed requires) before then. Full details
in Packaging.
Your data survives upgrades (runtime vs data)¶
Soulacy keeps runtime and data separate, so replacing the runtime on an upgrade never touches what you configured or saved.
- Runtime: the binary or container image. Thrown away and replaced on every upgrade.
- Data: everything you configure and Soulacy saves:
config.yaml, agents, memory, knowledge, secrets, skills, plugins. It lives outside the runtime: - Host installs: in
~/.soulacy/soulspace/, separate from the binary. - Docker/Compose: named volumes:
soulacy_data(the workspace),postgres_data,qdrant_data.docker compose up -dwith a new image keeps them. Never rundocker compose down -v: the-vdeletes those volumes. - Render / Coolify / Azure: a persistent disk mounted at
/home/soulacy/.soulacy. - Railway: you must attach a volume at
/home/soulacy/.soulacyyourself. See deploy/railway.
Because of this, the upgrade flows on this page (self-update, image pull, or platform redeploy) preserve everything. Schemas only move forward, so a newer version reads your existing data.
Safety check¶
On a container or managed platform, Soulacy verifies at startup and through
sy doctor that the workspace is on durable storage. If it isn't, it warns:
workspace is NOT on persistent storage; data will be lost on the next redeploy
That means the workspace is on the container's ephemeral filesystem. Mount a
persistent volume at /home/soulacy/.soulacy and redeploy before relying on it.
Back up your data¶
A backup is a copy of the workspace (and the DB volumes, if you use Postgres + Qdrant):
# Host / bind-mounted workspace
tar czf soulacy-backup.tgz -C ~/.soulacy soulspace
# Docker named volumes
docker run --rm -v soulacy_data:/d -v "$PWD":/b alpine tar czf /b/soulacy_data.tgz -C /d .
Restore by extracting back into the same location/volume before starting the new version.
Why upgrades are safe¶
Three guard layers protect the normal path:
1. Database schema versioning: additive only, never down¶
- Every SQLite store (workboard, costs, rbac, apikeys, dlq, checkpoints,
memory archive, action log, knowledge) records its schema version in a
shared
soulacy_schema_versiontable at boot. - Schema changes run through a shared migration helper: one transaction per step, versions ≤ current are skipped, and a failed step rolls back cleanly.
- Migrations are additive-only by default:
DROP/RENAMEstatements are refused unless a step explicitly opts in as destructive (reserved for deprecation cycles). - Versions never downgrade. A database touched by a newer build keeps its version; don't point an older binary at it and expect new tables to be understood.
2. API contract stability¶
Contract tests pin the /api/v1 response envelopes that the GUI, CLI,
and plugins depend on (agents, config, providers, skills, the plugin
install preview, the {"error": …} envelope, the 401 auth gate).
Additive fields are fine; removing or renaming a pinned key fails the
test suite and demands a deprecation cycle. The
event stream has the same rule: schema 1
is only bumped for breaking changes, with dual-publishing during the
transition.
3. Plugin SDK major gate & graceful fallbacks¶
- Plugin manifests may declare
sdk_major: <n>. A plugin built against a newer SDK major than the host is refused at load with an upgrade hint: it never crashes the gateway. - The loader warns-and-skips broken manifests, future schemas,
capability/credential violations, and stale install approvals. Every
refused plugin is published as a boot event (
type: error,stage: plugin-load) so the Logs GUI explains every silently absent plugin. - Crashed channel sidecars are contained by the supervisor (backoff restarts); the gateway keeps serving.
Back up before major upgrades¶
All state lives in one place: the workspace:
# Stop the gateway first so SQLite files are quiescent, then:
tar -czf soulacy-backup-$(date +%F).tar.gz ~/.soulacy
That captures config.yaml, agents, skills, plugins, all databases
(data/), memory, and the credential vault (secrets/). Restoring is
untarring it back. Verify what your workspace contains with
sy workspace info (Workspace Layout).
Minimum viable backup
If a full archive is too big, the irreplaceable parts are
config.yaml, agents/, data/, memory/, and secrets/.
Full reinstall from scratch¶
The repo ships reinstall-from-scratch.command (macOS) for when you want
a genuinely clean slate:
What it does, in order:
- Prompts for a typed confirmation: you must literally type
wipe. - Stops the gateway (port 18789, plus any launchd service).
- Deletes
~/.soulacyentirely: agents, memories, the encrypted credential vault, config.yaml. No backup is taken. - Rebuilds the GUI dist and binaries from the checkout (
make all). - Boots once so a fresh soulspace workspace is created, prints
sy workspace info, and offers thesy setupwizard.
Destructive: everything under ~/.soulacy is gone
This is a wipe, not an upgrade. If there is any chance you'll want your agents, conversation history, or vault back, take the backup above first. For a normal version bump, use the standard upgrade flow: never the reinstall script.
Rollback¶
Every upgrade run by install.sh first snapshots the outgoing install,
including the soulacy and sy binaries and the current config.yaml, into
~/.soulacy/backups/<timestamp>/ before overwriting anything. The five most
recent snapshots are kept (SOULACY_BACKUP_KEEP to change; SOULACY_BACKUP_DIR
to relocate).
If a new version misbehaves, roll straight back to the previous one:
./install.sh --rollback
# or from the one-liner:
curl -fsSL https://raw.githubusercontent.com/vmodekurti/soulacy-personal/main/install.sh | bash -s -- --rollback
This restores the previous binaries and their matching config.yaml. Your
current config is never destroyed: it's saved next to the restored one as
config.yaml.pre-rollback.<epoch> first. Restart the gateway afterward
(soulacy serve, or sy daemon stop && sy daemon start).
Data, not just the binary
Rollback swaps the binary and config back. Because schema migrations only move forward, if an upgrade migrated your workspace database, pair the rollback with the matching workspace backup (see below) for a fully consistent previous state.
See also¶
- Workspace Layout: what to back up
- macOS deployment · Linux / VPS · Docker