update, uninstall and cleanup-apps silently dropped any flag they did
not know, so 'serverkit update --help' ran a real update. --help now
prints usage and an unknown flag fails before anything runs.
A fresh panel no longer lets whoever reaches it first become admin:
registering the first account needs a one-time code the installer
prints (`serverkit setup-code` repeats it). SERVERKIT_ADMIN_EMAIL and
SERVERKIT_ADMIN_PASSWORD create the admin unattended instead.
Git apps now deploy what users expect: a repository with a compose
file deploys every service it declares, "Deploy latest" builds the
branch's newest commit, and push webhooks rebuild the containers.
Also fixes branch switching on single-branch clones, uploads of a
lone file, compose v1 port preflight, and CLI commands that ignored
their flags.
backend/cli.py gained `connect` (with status/doctor/disconnect) for the
ServerKit Cloud pairing but the `serverkit` wrapper never learned it, so
`serverkit connect` fell through to "unknown command" and the wrapper
dispatch census failed the Scripts CI suite. Add it to the help, the
dispatch case and bash completion; the passthrough test covers it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LGmHp7mZ2wSeA6z8BXZVxh
`serverkit doctor` assumed the frontend is the `serverkit-frontend`
container. On a host install that container never exists: install.sh has
nginx serve frontend/dist from disk, and the container only lives under the
opt-in `legacy-frontend` compose profile. Doctor flagged a phantom issue on
every host install, and `--fix` "repaired" it with a bare `docker compose
up -d` in /opt/serverkit, which does not start that container at all: it
builds the whole all-in-one panel image, which would then collide with the
systemd backend on :5000. start/stop/restart/status/logs carried the same
assumption.
Add `frontend_mode`, which reads the layout off the serverkit vhost (a
`root .../frontend/dist` is static, a `proxy_pass` on / is the legacy
container, neither is unknown), and make every command key off it. Static
installs are checked for the bundle nginx actually serves; when it is
missing doctor names the command that restores it (`serverkit update`)
instead of promising an auto-fix. The legacy path targets the profile's
service (`docker compose --profile legacy-frontend up -d frontend`), never
the panel image.
Tests: static install passes with no compose call, missing bundle under
--fix hints at update and still never calls compose, frontend_mode
classifies all three layouts, and an awk guard fails the suite if a bare
compose call from $INSTALL_DIR comes back.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LGmHp7mZ2wSeA6z8BXZVxh
A panel host filled to 100% (0 bytes free) purely from routine
`serverkit update` runs, and recovering it meant hand-running du and
sqlite. `serverkit disk` measures every reclaimable item and offers them
for selection: numbers, "safe", or "all".
Upgrade snapshots are grouped by timestamp and offered individually with
age and size, since one update writes both a tree backup and a database
copy under one stamp -- half a snapshot is not a restore point. They can
be picked by number, by age (--older-than N, or "older-than N" in the
prompt), or left to the "all-but-newest" default.
Two ordering rules are enforced regardless of selection order:
* filesystem reclaimers run BEFORE the database VACUUM. VACUUM rewrites
the database into a temp file beside it, so it needs free space equal
to the database -- on a full disk it is the one step that cannot go
first. It refuses with a clear message rather than failing halfway.
* `jobs` rows are deleted before `queue_messages`, and any message still
referenced by a surviving job is kept, so jobs.queue_message_id never
dangles.
in_flight messages and pending/failed jobs are never touched. /tmp is
only swept for entries matching mktemp naming, owned by the invoking
user, over 24h old AND holding an unpacked ServerKit tree. Docker prunes
dangling images and build cache only -- never tagged images or volumes.
Sizes are measured rather than estimated wherever measurement is
possible: docker reports its own reclaimed tally (shared layers make
dangling-image sizes overlap), and the journal and package caches report
before/after deltas -- apt-get clean empties the cache before a naive
count could see it, which would otherwise report 0 freed for real work.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Live test on the dev box caught 'serverkit list-apps --json' printing
the table: cmd_list_apps' hand-rolled flag mapping dropped everything
but --all, and cmd_list_users/cmd_list_servers dropped all args. All
three now forward "$@" (with -a kept as the legacy --all alias), and
test_cli.sh T20 drives the real executed-mode dispatch through a fake
venv with a recording python stub so any wrapper verb that eats flags
fails the suite.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
serverkit migrate-db forwarded the wrapper's own name to cli.py, which
only defines db-migrate — the command has failed with 'No such command'
since the rename. Fix the forward and stop the drift class entirely:
- cmd_panel_cli forwards Click-only verbs verbatim (manifest, services,
apps, repair, login-url, support-bundle, db-migrate/status/history;
panel-status/panel-doctor where a native bash name collides)
- serverkit completion prints a bash completion script; install.sh and
update.sh drop it into /etc/bash_completion.d (best-effort), the
uninstall lib removes it
- test_cli.sh T17-T19: migrate-db forwarding, dispatch reachability,
and a two-way parity guard (cli.py verbs <-> dispatch <-> completion);
test_update.sh T29 covers refresh_bash_completion
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- update --branch without a value errors loudly instead of dying
silently on shift (L5)
- cmd_start/cmd_restart mirror cmd_stop's systemctl/docker guards (L6)
- cmd_logs degrades past a dead/missing journalctl to docker logs (L7)
- cmd_add_site/remove_site/list_sites detect Debian vs conf.d nginx
layout; reloads are guarded with a manual-reload hint (L9)
- uninstall on a remnants-only box prints exact recovery commands (L4)
- portable port extraction (no grep -oP), read-at-EOF defaults,
compare_versions rejects unparsable as update-available, doctor
exits nonzero on issues (L12); BASH_SOURCE guard for testability
- new scripts/test/test_cli.sh (22 passed)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Critical install/update/uninstall hotfixes from the hardening plan
(docs/plans/INSTALLER_UPDATER_UNINSTALLER_HARDENING_PLAN.md §4):
- Firewall automation (G1): new sourceable scripts/lib/firewall.sh detects
firewalld/ufw/nftables/iptables and opens 80/443; install.sh runs
configure_firewall, update.sh re-ensures it idempotently. Exactly what we
open is recorded in /etc/serverkit/install-state.json (scripts/lib/state.sh)
so uninstall undoes only our rules. Fixes the #1 "installed but unreachable"
failure on fresh RHEL-family boxes.
- Accept Python 3.11 OR 3.12 (G2): locate_python now iterates
python3.12/3.11/3 and provision_python installs the distro package
(python3.11 on Debian 12, 3.12 on Ubuntu/Fedora, 3.11/3.12 on RHEL) before
ever falling back to a source compile.
- Use $PYTHON_BIN for secret generation instead of a bare python3.
- Distro-first Node 20 (replacing unconditional NodeSource): install the
distro nodejs+npm, fall back to NodeSource only when it's missing or < 18,
and validate node>=18 before continuing.
- Unify uninstall (G4): new scripts/lib/uninstall.sh is the single canonical
teardown routine shared by uninstall.sh and `serverkit uninstall` (they had
drifted — one ran `down -v`, one didn't). Default preserves user data
(volumes kept, DB snapshotted to backups); --purge/--delete-volumes removes
volumes+DB+data dirs; --keep-data preserves /etc + /var/lib + backups;
--yes/--dry-run added; honors SERVERKIT_DIR. Removes the apt lock-wait
drop-in on uninstall.
Tests: new scripts/test/test_install.sh (11 cases, source-able install.sh)
covers python/node detection, firewall dry-run + state, and the uninstall
data-handling matrix. Existing test_update.sh still green. shellcheck clean
(error + warning) on all new/changed shell.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Auto-generate SERVERKIT_ENCRYPTION_KEY in production when missing.
- Detect canonical domain from Host/X-Forwarded-Host; persist via SystemSettings.
- Expand CORS origins dynamically to include the canonical domain.
- Add SystemTab UI to view/save detected panel domain and HTTPS preference.
- Install script writes key, prompts for PANEL_DOMAIN, and sets nginx IP→domain redirect.
- Add backend tests for key bootstrap and domain detection helpers.
Introduce a deployment jobs system to support cross-server template installations and remote execution. Adds DeploymentJob and DeploymentJobLog models, DeploymentJobService for job lifecycle, and DeploymentPlanRunner to execute plans locally or via agents. TemplateService gains build_install_plan and related helpers to produce executable plans; Template installs can run async (202) or synchronously. Backend API/CLI updated: new deployment-jobs endpoints, apps API uses RemoteDockerService for remote compose actions, and CLI commands (deploy-template, deployment-status, list-servers) were added. Agent and config changes: file access validation with allowed_paths, file write create_dirs option, default allowed paths, and install script/systemd read-write paths updated to permit /var/lib/serverkit and /var/serverkit. Migration file for deployment_jobs schema included.
Generate private URL configs as location-only snippets in /etc/nginx/serverkit-locations instead of full server blocks. nginx_service.py: write location snippets (.conf) to LOCATIONS_DIR, ensure the locations directory, remove legacy separate server blocks, and add get_location_config to read these snippets (used for private URLs, Gitea, WordPress). install.sh & serverkit: only remove conflicting catch-all (server_name _) configs that are not ServerKit-managed (skip serverkit.conf and serverkit-*), strengthen the server_name regex, and create the serverkit-locations dir. cmd_doctor: add docker presence checks and avoid frontend checks when docker is missing; require root in --fix mode. Misc cleanup and minor refactors around enabling/disabling configs and reload handling.
Switch Gitea/WordPress to location-only snippets included into serverkit.conf and introduce /etc/nginx/serverkit-locations. NginxService now writes snippets to LOCATIONS_DIR, ensures the directory exists and removes legacy full-server configs to avoid server_name _ conflicts; validation blocks use of the reserved catch-all name. install.sh and the update path create the locations dir and remove any conflicting catch-all site configs. serverkit.conf proxy port updated (8080 -> 3847) and now includes the snippets directory. Add a new serverkit doctor command (with --fix) and help entries to run diagnostics and auto-fix common issues (nginx, docker, service health, ports, disk, DB, etc.).
Added calls to serverkit.ai to track successful installation, update, and uninstall events with the current version. This helps monitor usage and improve support.
Expanded the uninstall process to explicitly list all components and directories that will be removed, including nginx configs and ServerKit-specific data. Added informational messages and ensured nginx is reloaded if necessary. Clarified that system packages are not removed automatically.
Implements backend endpoints and service logic for managing standalone WordPress via Docker, including install, uninstall, start, stop, and restart. Adds Nginx integration for WordPress at /wordpress. Introduces a new frontend page for SSL certificate management with Certbot install, obtain, renew, and revoke actions. Updates the WordPress frontend page to support one-click install, status, and lifecycle management. Updates documentation and install instructions for improved clarity.
- Add 'serverkit update --branch <name>' to switch branches
- Add 'serverkit branch' command to show current branch
- Skip version check for non-main branches (dev mode)
- Show commit hash and branch in update output
- Warn users when running on non-main branch
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Expanded the cleanup and factory reset commands to remove orphaned Docker containers, networks, and volumes while preserving ServerKit infrastructure. Improved the list-apps command to optionally display all Docker containers with a new --all flag, and updated documentation and CLI help to reflect these changes.
Introduces new commands to manage applications and perform database migrations: `cleanup-apps`, `factory-reset`, and `migrate-db`. Updates documentation and help output to reflect these additions, and refactors `list-apps` to use the CLI for consistency.
- Add docker network prune and docker system prune before builds
- Use --no-cache for docker compose build to ensure fresh builds
- Add --force / -f flag to skip version check during update
- Helps prevent Docker networking issues
Usage: sudo serverkit update --force
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The compare_versions function returns non-zero values to indicate
version differences, but 'set -e' was causing bash to exit.
Now temporarily disabling exit-on-error during version comparison.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add VERSION file (starting at 1.0.0)
- Add GitHub Actions workflow to auto-bump version on push
- patch: default for all commits
- minor: commits starting with "feat:" or "feature:"
- major: commits starting with "BREAKING:" or "major:"
- Add CLI commands: version, check-update
- Update 'serverkit update' to show semantic versions
- Compare local vs remote version before updating
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Install script now copies bundled YAML templates to system directory
- Update command syncs templates on each update
- Creates /var/serverkit/apps directory for installed apps
Fixes 'No templates found' in Templates page.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- install.sh now starts with cd /tmp to ensure valid directory
- uninstall command moves to /tmp before deleting install dir
- Shows reinstall command after uninstall
Prevents 'cannot access parent directories' errors when running
install from a deleted directory.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The update command now restores chmod +x on serverkit and scripts
after git reset --hard to prevent permission denied errors.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Removed duplicate root templates/ folder
- Added PHP, Python, Node.js templates to marketplace (YAML format)
- Templates now installable via web UI Templates page
- CLI create-app now points users to web UI
- All 63 templates in one place: backend/templates/
New templates:
- php-app: Custom PHP 8.0-8.3 with Apache
- python-app: Custom Python 3.9-3.12 (Flask/FastAPI/Django)
- node-app: Custom Node.js 18-21 (Express/Next.js)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Host nginx routes domains to different apps
- ServerKit frontend moved to port 8080 (internal)
- New CLI commands: setup-proxy, add-site, list-sites, remove-site
- Site config templates for easy domain setup
- Install script now sets up nginx proxy automatically
Usage:
sudo serverkit add-site test.example.com 3000
sudo serverkit list-sites
sudo serverkit remove-site test.example.com
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Removed docker-compose.prod.yml and updated docker-compose.yml to only run the frontend in Docker, with the backend now running as a systemd service on the host. Updated nginx and frontend nginx.conf to proxy API requests to the backend on the host. Added serverkit-backend.service for systemd management of the backend. Refactored install.sh and serverkit CLI to support the new architecture, including improved installation, update, and management commands.
Introduces a comprehensive Bash CLI tool (`serverkit`) for managing ServerKit services, users, and database operations. Adds a quick-install script (`install.sh`) for Ubuntu, a sample environment file (`.env.example`), and a Python CLI backend (`backend/cli.py`) for administrative tasks. Updates Docker Compose for production readiness, enhances deployment documentation, and adds Click to backend requirements. Also updates references to the correct GitHub repository.