Update Mechanism¶
How the "Check for Updates" button works to update the application without rebuilding the Docker image.
Overview¶
The application code, model catalogs, and workflows live in the Git repository and are synced to the pod on demand. When you click "Check for Updates" on the Home page, the backend:
- Fetches the latest code from GitHub into
.repo/ - Compares component versions between local and remote
- Copies changed components to the working directories
- Restarts itself if the backend code changed
This allows you to push code changes to GitHub and have them appear on the pod within seconds, without rebuilding or re-pulling the Docker image.
What Can Be Updated¶
| Component | Updated by | Restart? | When visible? |
|---|---|---|---|
| Backend (Python code) | Copied from .repo/backend/ |
Yes (auto) | After ~2 second restart |
| Frontend (HTML/CSS/JS) | Copied from .repo/frontend/ |
No | Next page load |
Model catalog (models.json) |
Copied from .repo/catalogs/ |
No | Next API call (after _reload_models()) |
LoRA catalog (loras.json) |
Same | No | Same |
LLM catalog (llm.json) |
Same | No | Same |
| Workflows | Copied from .repo/workflows/ |
No | Next API call |
| Runtime (Docker image) | Cannot be updated this way | N/A | Must pull new image |
Update Flow¶
Step 1: Git Fetch¶
Executed in STUDIO_DIR/.repo/. This downloads the latest commit from GitHub without modifying the working tree. If the fetch fails (for example, after a history rewrite), the backend deletes .repo/ and re-clones from scratch.
Step 2: Read Remote Version¶
Reads the version.json from the fetched commit without checking it out. This gives us the remote component versions to compare against.
Step 3: Runtime Compatibility Check¶
If remote.min_runtime > local.RUNTIME_VERSION, the update returns blocked: true with a message telling the user to pull a newer Docker image. No changes are made.
Step 4: Compare Versions¶
For each component in version.json, the backend compares the remote version with the local version:
- Integers (models, loras, workflows): remote
>local → needs update - Semver strings (backend, frontend): parsed as tuples and compared
- Type mismatch: forces update (handles schema migrations)
Components listed in skip_components (unchecked toggles on the Home page) are skipped.
If nothing needs updating, the response says "Everything up to date" and no maintenance mode is triggered.
Step 5: Maintenance Mode¶
If updates are needed, the backend enters maintenance mode. In this mode:
- All API calls return
503 {"detail": "System is updating, please wait..."} - All page requests return an HTML page with "Updating..." message and auto-reload script (every 3 seconds)
- The WebSocket continues to function for connected clients
Step 6: Git Reset¶
Updates .repo/ to match the remote exactly. This is safe because .repo/ is never modified locally — it's purely a staging area.
Step 7: Copy Changed Components¶
The backend copies only the components that need updating:
Backend:
Frontend:
Catalogs (models, loras, llm): individual file copies with shutil.copy2.
Workflows: copy index.json + each workflow directory (rmtree + copytree per workflow).
Step 8: Merge Version JSON¶
The local version.json is updated with remote versions, except for skipped components which keep their local versions. This ensures that skipped components don't appear as "needs update" on the next check.
Step 9: Reload or Restart¶
- If catalogs changed:
_reload_models()refreshes in-memory catalog data - If backend changed: schedule restart via
subprocess.Popen+os._exit(0)-- spawns a new uvicorn process and terminates the current one - If no restart needed: exit maintenance mode immediately
Restart mechanism
The backend restart no longer uses os.execv. Instead, it spawns a new uvicorn process with subprocess.Popen(start_new_session=True) and then calls os._exit(0) to terminate the current process. This avoids issues with os.execv and signal handlers in the uvicorn worker.
Component Update Toggles¶
The Home page shows toggle switches next to each component. Unchecked components are sent as skip_components in the update request:
This lets users skip components they don't want to update (for example, keeping a locally modified catalog).
What Triggers a Docker Rebuild¶
Certain files in the repository trigger a Docker image rebuild on GitHub Actions when pushed:
| Path | Triggers rebuild? |
|---|---|
docker/* (Dockerfile, start.sh, bootstrap.py, nodes.txt) |
Yes |
.github/workflows/* |
Yes |
backend/** |
No |
frontend/** |
No |
catalogs/** |
No |
workflows/** |
No |
version.json |
No |
*.md |
No |
chrome-extension/** |
No |
docs/** |
No |
mkdocs.yml |
No |
This means you can push backend, frontend, catalog, and workflow changes all day without waiting for a Docker build. Only infrastructure changes (Dockerfile, custom nodes, startup scripts) trigger a rebuild.