appgarden

内容来源:SKILL.md(标准 Skill 格式) · 原始地址 · 查看安装指南

原始内容


name: appgarden description: > Deploy and manage web applications on remote servers with AppGarden. Use when the user asks about deploying apps, configuring appgarden.toml, managing AppGarden servers/apps, viewing logs/status/metadata, or exposing local work through AppGarden tunnels. user-invocable: false

AppGarden CLI Skill

Use this skill when deploying or operating web applications with the appgarden CLI.

AppGarden is an agentless deploy tool: the local CLI connects over SSH, prepares the remote server, uploads or clones app source, writes Caddy/systemd/Docker files, and records state under the server's AppGarden root (default /srv/appgarden).

Before running commands

  • Prefer running from the AppGarden repo checkout when developing/testing AppGarden itself:

    cd /path/to/appgarden && uv run appgarden ...
    

    If AppGarden is installed in the environment, appgarden ... is also fine.

  • Read-only/safe discovery commands are fine to run without confirmation: appgarden --help, appgarden version, appgarden config show, appgarden server list, appgarden server ping, appgarden apps list, appgarden apps status, appgarden apps logs, appgarden tunnel list.

  • Ask before commands that create, mutate, deploy, restart, stop, delete, or expose services: server add, server remove, server default, server init, deploy, apps start|stop|restart|redeploy|remove, apps meta set|replace|remove, tunnel open|close|cleanup.

  • Be especially careful with:

    • appgarden server init --include ssh: hardens SSH configuration and reloads sshd.
    • appgarden server init --include firewall: enables UFW and changes firewall policy.
    • appgarden apps remove NAME --yes: deletes app resources; add --keep-data only when preserving the app's data/ directory is intended.
    • appgarden deploy: may overwrite uploaded source for an existing app and may update Caddy/systemd/Docker resources.
    • appgarden tunnel open: publicly exposes a local port/file/directory through the configured server until stopped.

Configuration locations

Local config is stored at:

~/.config/appgarden/config.toml

Server config shape:

default_server = "myserver"

[defaults]
method = "dockerfile"
container_port = 3000

[servers.myserver]
host = "203.0.113.10"              # or hcloud_name + hcloud_context
ssh_user = "root"
ssh_key = "~/.ssh/id_rsa"
domain = "apps.example.com"
app_root = "/srv/appgarden"        # optional; defaults to /srv/appgarden

[servers.myserver.init]
skip = ["upgrades"]               # optional persistent init skips

Remote state and resources are under app_root, usually:

/srv/appgarden/garden.json          # app registry
/srv/appgarden/apps/<name>/         # per-app files/source/app.json/.env/data
/srv/appgarden/caddy/apps/*.caddy   # deployed app Caddy snippets
/srv/appgarden/caddy/tunnels/*.caddy
/srv/appgarden/tunnels/active.json

appgarden config show prints the local config. Use --server <name> / -s <name> or APPGARDEN_SERVER on commands that target a server; otherwise AppGarden uses default_server.

Multiple gardens on one host: you can run several AppGarden gardens on the same box by giving each a distinct app_root (e.g. /srv/appgarden and /srv/appgarden-proto). Per-garden state (garden.json), Caddy snippets, and the /etc/caddy/Caddyfile managed block are all keyed per app_root, so server init for one garden won't disturb another. Ports, however, are a box-global resource — every garden shares the host's TCP port space — so port allocations live in a single host-level registry at /var/lib/appgarden/ports.json, shared by all gardens under one lock. This is what prevents two gardens handing out the same host port; you don't configure port ranges. Caveats: systemd unit and Docker container names are box-global (appgarden-<name>), so app names must be unique across gardens on one host. If you're upgrading a host that pre-dates the shared registry, run appgarden server init once per existing garden — init reconciles each garden's already-deployed app ports into the shared registry (idempotent).

Server setup

A server must be configured, reachable over SSH, and have DNS pointing at it.

appgarden server add myserver \
  --host <server-ip-or-hostname> \
  --ssh-user root \
  --ssh-key ~/.ssh/id_rsa \
  --domain apps.example.com

# Hetzner alternative: resolve IP through hcloud instead of --host
appgarden server add myserver \
  --hcloud-name <hetzner-server-name> \
  --hcloud-context <hcloud-context> \
  --domain apps.example.com

appgarden server list
appgarden server ping myserver
appgarden server default myserver

For subdomain deployments and tunnels, create wildcard DNS such as *.apps.example.com pointing at the server IP. Explicit custom hostnames also need DNS pointing at the server.

Initializing a server

Run once per server:

appgarden server init myserver

Current server init behavior:

  • Optional steps on by default; skip with --skip: update, docker, caddy, upgrades.
  • Opt-in steps off by default; enable with --include: firewall, ssh, fail2ban, group.
  • Essential steps always run: configure the AppGarden Caddyfile import block, create directories, install the privileged helper/sudoers entry, set ownership for the deploy user, initialize state files, and start Docker/Caddy where available.

Examples:

appgarden server init myserver --minimal              # essential steps only
appgarden server init myserver --skip upgrades        # skip unattended-upgrades
appgarden server init myserver --include group        # create/use appgarden group for non-root deploy users
appgarden server init myserver --include firewall     # configure UFW; confirm first
appgarden server init myserver --include ssh          # harden sshd; confirm first

For non-root SSH users, server init installs /usr/local/bin/appgarden-privileged and a sudoers rule restricted to AppGarden-scoped privileged operations.

Preferred deployment flow: appgarden.toml

Create appgarden.toml in the project root and deploy named environments.

[app]
name = "myapp"
slug = "my-app"                  # optional; used by {app.slug}; falls back to name
server = "myserver"
method = "dockerfile"            # static | command | dockerfile | docker-compose | auto
source = "."                     # local path or git URL
container_port = 3000
subdomain = "{app.slug}"         # or path/url; supports placeholders
exclude = ["node_modules", ".git"]
gitignore = true                  # local uploads honor .gitignore by default
meta = { team = "frontend" }

[environments.production]
branch = "main"
subdomain = "{app.slug}"
env = { NODE_ENV = "production" }

[environments.staging]
branch = "staging"
subdomain = "{app.slug}-staging"
env_file = ".env.staging"
meta = { visibility = "internal" }

Deploy:

appgarden deploy production
appgarden deploy staging
appgarden deploy --all-envs
appgarden deploy --project /path/to/project production

If appgarden.toml is present and no environment/name is provided, appgarden deploy deploys all environments. If a positional environment is provided, it must exist.

appgarden.toml semantics

  • [app].name is required. Deployed app names are derived from it: production uses the base name, every other environment uses <name>-<env>. Example: myapp and myapp-staging.
  • Values cascade in this order: hardcoded defaults < global [defaults] in ~/.config/appgarden/config.toml < [app] defaults < [environments.<name>] < CLI flags / APPGARDEN_* environment variables.
  • Dict fields env and meta merge by key; environment/CLI values override earlier keys.
  • List fields exclude and volumes concatenate and deduplicate across layers.
  • String values in project/environment config support placeholders: {app.name}, {app.slug}, {env.name}.
  • Relative local source and env_file values resolve relative to the project directory or the directory containing the explicit --project file.

Useful fields in [app] or [environments.<name>]:

Field Meaning
server Configured server name.
method static, command, dockerfile, docker-compose, or auto. Default is static.
source Local directory/path or git URL. Required for static, dockerfile, docker-compose, auto; optional for command.
url Explicit public hostname/path, e.g. example.com or example.com/docs.
subdomain Prefix combined with domain or the server domain, e.g. docs -> docs.apps.example.com.
path Path under domain or server domain, e.g. docs -> apps.example.com/docs.
domain Override the server's base domain for subdomain/path.
port Host port; omitted means AppGarden auto-allocates from its port state.
container_port Port exposed inside Dockerfile/auto containers. Default is 3000.
cmd Start command for command and auto methods. Required for both.
setup_cmd Install/build command for auto; overrides the detected runtime default.
branch Git branch for git sources.
env Inline environment variables.
env_file Local dotenv file.
meta Arbitrary metadata stored with the app and shown in status.
exclude Rsync exclude patterns for local uploads; also reused by apps redeploy.
gitignore Whether local uploads pass rsync's .gitignore filter. Default true; CLI override is --no-gitignore.
volumes Docker volume mounts (host:container[:opts]) for dockerfile and auto.
created_at, updated_at, repo Optional metadata overrides. Dates accept short date, ISO string, or TOML dates; repo is otherwise auto-detected from local git origin when possible.

URL resolution order is: explicit url; otherwise subdomain + domain; otherwise path + domain. For subdirectory routing (domain/path), multiple apps on the same domain share a generated Caddy config and AppGarden checks URL conflicts.

Ad-hoc deployment without appgarden.toml

Use --name and flags. Most flags also have APPGARDEN_* environment variable equivalents (APPGARDEN_NAME, APPGARDEN_SERVER, APPGARDEN_METHOD, APPGARDEN_SOURCE, APPGARDEN_URL, APPGARDEN_SUBDOMAIN, APPGARDEN_PATH, APPGARDEN_DOMAIN, APPGARDEN_PORT, APPGARDEN_CONTAINER_PORT, APPGARDEN_CMD, APPGARDEN_SETUP_CMD, APPGARDEN_BRANCH, APPGARDEN_ENVVAR_FILE, APPGARDEN_PROJECT, APPGARDEN_ALL_ENVS).

# Static site / SPA
appgarden deploy --name mysite --method static --source ./dist --subdomain mysite

# Dockerfile app
appgarden deploy --name myapp --method dockerfile --source . --container-port 3000 --subdomain myapp

# Auto-generated Dockerfile; runtime is detected from package.json, requirements.txt,
# pyproject.toml, Gemfile, go.mod, or Cargo.toml. --cmd is required.
appgarden deploy --name myapp --method auto --source . --cmd "npm start" --subdomain myapp

# Bare process managed by systemd. --source is optional; --cmd is required.
appgarden deploy --name myapi --method command --source ./api --cmd "python app.py" --subdomain myapi

# Docker Compose stack; your compose file is used directly.
appgarden deploy --name mystack --method docker-compose --source ./project --subdomain mystack

# Explicit path routing instead of subdomain routing
appgarden deploy --name docs --method static --source ./docs --url apps.example.com/docs

# Env, metadata, excludes, and volumes
appgarden deploy --name myapp --method dockerfile --source . --subdomain myapp \
  --envvar NODE_ENV=production \
  --envvar-file .env.production \
  --meta team=backend --meta visibility=internal \
  --exclude node_modules --exclude .git \
  --volume ./data:/app/data \
  --no-gitignore

Deployment method notes:

Method Use for Required inputs Runtime behavior
static HTML/CSS/JS/SPAs source Upload/clone source and serve directly with Caddy.
command A local process without Docker cmd (source optional) Creates a systemd service with PORT set to allocated/explicit port.
dockerfile Projects with a Dockerfile source Builds image remotely, writes AppGarden compose file, runs through systemd.
docker-compose Existing compose projects source Runs your docker compose up/down through systemd; volumes config is not injected.
auto Simple Node/Python/Ruby/Go/Rust projects source, cmd Detects runtime, writes Dockerfile, builds image, writes compose file.

For local uploads, AppGarden uses rsync with --delete, honors .gitignore by default, and applies any --exclude/exclude patterns. For git sources, it clones the repo on the remote and optionally checks out the requested branch.

Environment variable precedence for deployed app .env files is: appgarden.toml env < env_file content < CLI --envvar values.

Managing deployed apps

appgarden apps list [-s server] [--short]
appgarden apps status <name> [-s server]
appgarden apps logs <name> [-s server] [-n 100]
appgarden apps restart <name> [-s server]
appgarden apps redeploy <name> [-s server]
appgarden apps stop <name> [-s server]
appgarden apps start <name> [-s server]
appgarden apps remove <name> [-s server] [--keep-data] [--yes]

Operational guidance:

  • Use apps list first when you do not know the exact app name.

  • Use apps status to inspect URL, routing, method, port, repo/source, timestamps, and metadata.

  • Use apps logs NAME -n 200 before restarting when debugging.

  • apps redeploy updates source: git apps run git pull (with the stored branch if present); local-source apps re-upload with stored exclude/gitignore; Dockerfile/auto apps rebuild images and preserve stored volumes; static apps reload Caddy.

  • If changing an app name in appgarden.toml, remove the old app first to avoid URL/Caddy conflicts:

    appgarden apps remove old-name --yes
    appgarden deploy production
    

Metadata

Metadata is arbitrary JSON-compatible key/value data stored with the app.

appgarden apps meta get myapp [-s server]
appgarden apps meta set myapp --meta team=backend --meta tier=premium [-s server]
appgarden apps meta replace myapp --json '{"team":"frontend"}' [-s server]
appgarden apps meta remove myapp tier visibility [-s server]

In appgarden.toml, meta dictionaries merge like env dictionaries:

[app]
name = "myapp"
meta = { team = "backend", visibility = "internal" }

[environments.production]
meta = { visibility = "public" }
# result: { team = "backend", visibility = "public" }

Localhost tunnels

Use tunnels to expose a local dev server, file, directory, or command through the remote server with HTTPS. A tunnel blocks until Ctrl+C unless --close-on-cmd-exit is used with --cmd/--serve.

# Expose an existing local server. URL omitted => random three-word subdomain.
appgarden tunnel open 3000

# Use server domain subdomain shorthand or an explicit URL
appgarden tunnel open 3000 --subdomain preview
appgarden tunnel open 3000 --url preview.apps.example.com

# Run a local command while the tunnel is open
appgarden tunnel open 3000 --cmd "npm run dev"
appgarden tunnel open 3000 --cmd "npm run dev" --close-on-cmd-exit

# Serve a local file or directory. LOCAL_PORT is optional with --serve.
appgarden tunnel open --serve ./dist --subdomain docs-preview
appgarden tunnel open --serve ./dist --include "*.html" --exclude "node_modules"

appgarden tunnel list [-s server]
appgarden tunnel close <tunnel-id> [-s server]
appgarden tunnel cleanup [-s server]

--cmd and --serve are mutually exclusive. --include/--exclude only apply when --serve points to a directory. Tunnels allocate a remote port, write a Caddy snippet under caddy/tunnels, and register state in tunnels/active.json; cleanup removes those resources.

Troubleshooting workflow

  1. Confirm config and server targeting:

    appgarden config show
    appgarden server list
    appgarden server ping <server>
    
  2. Inspect app state and logs:

    appgarden apps list --short
    appgarden apps status <app>
    appgarden apps logs <app> -n 200
    
  3. Redeploy or restart only after reading the error:

    appgarden apps redeploy <app>
    appgarden apps restart <app>
    
  4. Common local-source upload issues:

    • rsync must be installed locally.
    • Encrypted SSH keys need an agent: eval $(ssh-agent) && ssh-add ~/.ssh/id_rsa.
    • Permission errors on non-root deploys usually mean rerun appgarden server init --include group or fix remote ownership.

Reference files in this repository

The generated src/appgarden/*.py files say "AUTOGENERATED"; implementation edits should usually be made in the corresponding pts/appgarden/*.pct.py source files. For skill/use questions, read these files as needed:

  • README.md — user-facing guide and CLI reference.
  • src/appgarden/cli.py / pts/appgarden/10_cli.pct.py — command definitions and options.
  • src/appgarden/config.py / pts/appgarden/00_config.pct.py — local config model and default config path.
  • src/appgarden/environments.py / pts/appgarden/08_environments.pct.pyappgarden.toml parsing, merge semantics, placeholders.
  • src/appgarden/server.py / pts/appgarden/04_server.pct.py — server init steps.
  • src/appgarden/deploy.py / pts/appgarden/05_deploy.pct.py — deployment behavior and remote state.
  • src/appgarden/apps.py / pts/appgarden/06_apps.pct.py — app lifecycle and redeploy behavior.
  • src/appgarden/routing.py / pts/appgarden/03_routing.pct.py — Caddy routing and URL conflict behavior.
  • src/appgarden/tunnel.py / pts/appgarden/09_tunnel.pct.py — tunnel behavior.