d4659146fc
Per-stage env var overrides with encryption for secrets.
Volume mounts with shared/isolated modes (isolated appends
/{stage}-{tag}/ to source path). Store CRUD, API endpoints,
and frontend editors for both. Env merge during deploy.
513 lines
23 KiB
Markdown
513 lines
23 KiB
Markdown
# Docker Watcher — Implementation Plan
|
|
|
|
## Overview
|
|
|
|
A self-hosted tool that automates Docker container deployment with Nginx Proxy Manager integration. Detects new images from Gitea/GitHub registries, deploys containers, and configures reverse proxy routing — all from a web dashboard. Supports multiple simultaneous versions of the same project. DNS is handled by a Cloudflare wildcard record (`*.dolgolyov-family.by`) — no per-project DNS management needed.
|
|
|
|
## Architecture
|
|
|
|
```text
|
|
Gitea CI → pushes image → Registry
|
|
│ ↓
|
|
│ Docker Watcher (Go)
|
|
│ ├── Secret webhook URL (instant)
|
|
│ └── Registry poller (fallback)
|
|
│ ↓
|
|
└── or: POST /api/webhook/<secret-uuid>
|
|
with {"image": "registry/org/app:tag"}
|
|
↓
|
|
Known project? ──────────────────┐
|
|
↓ yes ↓ no
|
|
Match tag → stage Auto-create project
|
|
↓ with defaults from
|
|
auto_deploy? image inspection
|
|
↓ yes ↓ no (EXPOSE, labels)
|
|
Deploy now Notify, wait ↓
|
|
↓ for UI trigger Deploy with defaults
|
|
↓ ↓
|
|
Pull image
|
|
Start new container on shared network
|
|
(old container stays if multi-instance)
|
|
↓
|
|
NPM API: create proxy host (if first deploy for this subdomain)
|
|
(DNS already handled by Cloudflare wildcard *.domain)
|
|
↓
|
|
Health check
|
|
→ success: done, notify
|
|
→ failure: remove new container, alert
|
|
```
|
|
|
|
## Decisions
|
|
|
|
| Decision | Choice | Rationale |
|
|
|----------|--------|-----------|
|
|
| Language | Go | Single binary, excellent Docker SDK, low resource usage |
|
|
| Web UI | SvelteKit (embedded in Go binary) | User's existing stack, lightweight |
|
|
| Reverse proxy | Nginx Proxy Manager | Already deployed, API available |
|
|
| DNS | Cloudflare wildcard `*.{domain}` | One-time setup, all subdomains auto-resolve |
|
|
| Routing | Subdomain-based | No sub-path issues with SPAs |
|
|
| Image detection | Secret webhook URL + polling | Webhook for speed, polling as fallback |
|
|
| Config storage | SQLite (YAML for initial seed only) | Editable via UI, no manual file editing |
|
|
| Credentials | Encrypted in SQLite (AES-256) | Single ENCRYPTION_KEY env var |
|
|
| Webhook auth | Secret UUID in URL | No tokens needed, simple CI integration |
|
|
| Multi-instance | Yes | Multiple tags of same project can run simultaneously |
|
|
| Deployment target | Same TrueNAS host | Docker socket mounted |
|
|
|
|
## Subdomain Convention
|
|
|
|
| Type | Pattern | Example |
|
|
|------|---------|---------|
|
|
| Dev (default) | `stage-dev-{project}.{domain}` | `stage-dev-web-app-launcher.dolgolyov-family.by` |
|
|
| Dev (specific tag) | `stage-dev-{project}-{tag}.{domain}` | `stage-dev-web-app-launcher-abc123.dolgolyov-family.by` |
|
|
| Release (default) | `stage-rel-{project}.{domain}` | `stage-rel-web-app-launcher.dolgolyov-family.by` |
|
|
| Release (specific tag) | `stage-rel-{project}-{tag}.{domain}` | `stage-rel-web-app-launcher-v1-2-0.dolgolyov-family.by` |
|
|
| Production | `{custom}.{domain}` | `launcher.dolgolyov-family.by` |
|
|
|
|
Tags are sanitized for DNS: dots → dashes, lowercase, truncated to fit DNS limits.
|
|
|
|
## Configuration
|
|
|
|
### First Launch
|
|
|
|
```text
|
|
YAML seed file exists? → import into SQLite → done
|
|
No YAML? → empty state, configure everything via UI
|
|
```
|
|
|
|
After import, all configuration lives in SQLite and is managed via the Web UI.
|
|
YAML is never read again unless user clicks "Re-import config" or "Export config".
|
|
|
|
### Seed Config Format (optional)
|
|
|
|
```yaml
|
|
global:
|
|
domain: dolgolyov-family.by
|
|
server_ip: 93.84.96.191
|
|
network: staging-net
|
|
subdomain_pattern: "stage-{stage}-{project}"
|
|
notification_url: https://notify.dolgolyov-family.by/webhook
|
|
npm:
|
|
url: http://npm:81
|
|
email: docker-watcher@dolgolyov-family.by
|
|
password: "npm-password-here"
|
|
registries:
|
|
gitea:
|
|
url: https://git.dolgolyov-family.by
|
|
type: gitea
|
|
token: "gitea-token-here"
|
|
|
|
projects:
|
|
web-app-launcher:
|
|
registry: gitea
|
|
image: git.dolgolyov-family.by/alexei/web-app-launcher
|
|
port: 3000
|
|
healthcheck: /api/health
|
|
env:
|
|
NODE_ENV: production
|
|
stages:
|
|
dev:
|
|
tag_pattern: "dev-*"
|
|
auto_deploy: true
|
|
max_instances: 5
|
|
rel:
|
|
tag_pattern: "v*"
|
|
auto_deploy: false
|
|
max_instances: 2
|
|
prod:
|
|
tag_pattern: "v*"
|
|
auto_deploy: false
|
|
confirm: true
|
|
promote_from: rel
|
|
max_instances: 2
|
|
subdomain: launcher
|
|
```
|
|
|
|
## Web UI Sections
|
|
|
|
### Dashboard
|
|
|
|
Overview of all projects with their running instances:
|
|
- Project name, running instance count, latest activity
|
|
- Quick status indicators (healthy / stopped / failing)
|
|
- "Quick Deploy" button for ad-hoc image deployment
|
|
|
|
### Project Detail
|
|
|
|
Per-project view with stages and instances:
|
|
- Each stage shows all running instances with: tag, status, URL, uptime
|
|
- Controls per instance: Stop, Start, Restart, Remove
|
|
- "Deploy new version" dropdown — lists available tags from registry
|
|
- Deploy history log
|
|
|
|
### Quick Deploy
|
|
|
|
For deploying images not yet configured as projects:
|
|
1. Paste image URL (e.g., `git.dolgolyov-family.by/alexei/my-app:dev-abc123`)
|
|
2. Docker Watcher pulls and inspects image (EXPOSE port, HEALTHCHECK, labels)
|
|
3. Pre-fills form with sensible defaults (project name, port, stage, subdomain)
|
|
4. User reviews, tweaks, clicks "Deploy"
|
|
5. Project is auto-created in the DB for future use
|
|
|
|
### Settings
|
|
|
|
- **Registries** — add/edit/delete registries, test connection
|
|
- **Credentials** — NPM, registry tokens (encrypted, shown as `••••••••`)
|
|
- **Global** — domain, server IP, Docker network, subdomain pattern, polling interval
|
|
- **Notifications** — webhook URL
|
|
- **Webhook URL** — shows the secret deploy URL, "Regenerate" button
|
|
|
|
### Projects Config
|
|
|
|
- Add / edit / delete projects via UI
|
|
- Configure image, port, healthcheck, env vars, volumes per project
|
|
- Add / remove stages, set tag patterns, auto-deploy, subdomain overrides, max instances
|
|
|
|
## Project Structure
|
|
|
|
```text
|
|
docker-watcher/
|
|
├── cmd/
|
|
│ └── server/
|
|
│ └── main.go # Entry point
|
|
├── internal/
|
|
│ ├── config/
|
|
│ │ ├── config.go # YAML seed parsing
|
|
│ │ └── config_test.go
|
|
│ ├── docker/
|
|
│ │ ├── client.go # Docker Engine API wrapper
|
|
│ │ ├── container.go # Create, start, stop, remove, inspect
|
|
│ │ └── client_test.go
|
|
│ ├── npm/
|
|
│ │ ├── client.go # NPM API client (auth, CRUD proxy hosts)
|
|
│ │ └── client_test.go
|
|
│ ├── registry/
|
|
│ │ ├── registry.go # Interface
|
|
│ │ ├── gitea.go # Gitea registry implementation
|
|
│ │ ├── github.go # GitHub Container Registry (future)
|
|
│ │ ├── poller.go # Periodic tag polling
|
|
│ │ └── registry_test.go
|
|
│ ├── deployer/
|
|
│ │ ├── deployer.go # Orchestrates full deploy flow
|
|
│ │ ├── rollback.go # Rollback on failure
|
|
│ │ └── deployer_test.go
|
|
│ ├── health/
|
|
│ │ ├── checker.go # HTTP health checks with retries
|
|
│ │ └── checker_test.go
|
|
│ ├── notify/
|
|
│ │ ├── notifier.go # Webhook notifications
|
|
│ │ └── notifier_test.go
|
|
│ ├── webhook/
|
|
│ │ ├── handler.go # Secret URL webhook receiver
|
|
│ │ └── handler_test.go
|
|
│ ├── api/
|
|
│ │ ├── router.go # HTTP API for web UI
|
|
│ │ ├── projects.go # Project CRUD endpoints
|
|
│ │ ├── registries.go # Registry CRUD endpoints
|
|
│ │ ├── settings.go # Global settings endpoints
|
|
│ │ ├── instances.go # Instance start/stop/restart/remove
|
|
│ │ ├── deploys.go # Deploy + quick deploy endpoints
|
|
│ │ └── middleware.go # Auth, logging, CORS
|
|
│ ├── store/
|
|
│ │ ├── store.go # SQLite schema, migrations
|
|
│ │ ├── projects.go # Project queries
|
|
│ │ ├── instances.go # Instance queries
|
|
│ │ ├── registries.go # Registry queries
|
|
│ │ ├── settings.go # Settings queries
|
|
│ │ ├── deploys.go # Deploy history queries
|
|
│ │ └── store_test.go
|
|
│ └── crypto/
|
|
│ └── crypto.go # AES-256 encrypt/decrypt for credentials
|
|
├── web/ # SvelteKit frontend
|
|
│ ├── src/
|
|
│ │ ├── routes/
|
|
│ │ │ ├── +page.svelte # Dashboard
|
|
│ │ │ ├── projects/
|
|
│ │ │ │ ├── +page.svelte # Projects list + add
|
|
│ │ │ │ └── [id]/
|
|
│ │ │ │ └── +page.svelte # Project detail + instances
|
|
│ │ │ ├── deploy/
|
|
│ │ │ │ └── +page.svelte # Quick deploy
|
|
│ │ │ └── settings/
|
|
│ │ │ ├── +page.svelte # Global settings
|
|
│ │ │ ├── registries/
|
|
│ │ │ │ └── +page.svelte
|
|
│ │ │ └── credentials/
|
|
│ │ │ └── +page.svelte
|
|
│ │ ├── lib/
|
|
│ │ │ ├── api.ts # API client
|
|
│ │ │ ├── types.ts # Shared types
|
|
│ │ │ └── components/ # Reusable UI components
|
|
│ │ └── app.html
|
|
│ ├── package.json
|
|
│ ├── svelte.config.js
|
|
│ └── vite.config.ts
|
|
├── docker-watcher.example.yaml # Example seed config
|
|
├── Dockerfile
|
|
├── docker-compose.yml
|
|
├── go.mod
|
|
└── go.sum
|
|
```
|
|
|
|
## Implementation Phases
|
|
|
|
### Phase 1: Foundation ✅
|
|
|
|
Core infrastructure — store, config import, Docker client, NPM client.
|
|
|
|
1. **Go project init** — go.mod, directory structure, dependencies
|
|
2. **SQLite store** — schema, migrations, CRUD for projects/registries/settings/instances/deploys
|
|
3. **Crypto** — AES-256 encrypt/decrypt for credential storage
|
|
4. **Config seed loader** — parse YAML, import into SQLite on first launch
|
|
5. **Docker client** — connect to socket, pull image, inspect image, list/start/stop/remove containers, manage networks
|
|
6. **NPM client** — authenticate (JWT), create/update/delete proxy hosts, list existing hosts
|
|
|
|
### Phase 2: Detection & Deployment (Registry & Poller ✅, Webhook ✅, Deployer ✅)
|
|
|
|
The core loop — detecting new images and deploying them.
|
|
|
|
8. **Registry client** ✅ — Gitea registry API: list tags for an image, detect new tags
|
|
9. **Poller** ✅ — periodic check for new tags matching configured patterns
|
|
10. **Secret webhook handler** ✅ — UUID-based URL, receives image push notifications, auto-creates unknown projects
|
|
11. **Deployer** ✅ — orchestrate: pull → start container → NPM proxy → health check
|
|
12. **Multi-instance support** ✅ — multiple versions per project/stage, tag-based subdomains, max_instances limit
|
|
13. **Health checker** ✅ — HTTP GET with retries and timeout (3 retries, 5s interval, 10s timeout)
|
|
14. **Rollback** ✅ — on health check failure: remove new container, clean up NPM, alert
|
|
15. **Notifications** ✅ — send webhook on deploy success/failure (fire-and-forget)
|
|
|
|
### Phase 3: Web UI
|
|
|
|
Full dashboard for visibility, manual control, and configuration.
|
|
|
|
16. **API layer** — REST endpoints for all CRUD operations + deploy/control actions
|
|
17. **SvelteKit dashboard** — project overview, instance status, quick status indicators
|
|
18. **Project detail view** — stages, instances, controls (stop/start/restart/remove), deploy history
|
|
19. **Quick Deploy page** — paste image URL, auto-inspect, pre-fill form, one-click deploy
|
|
20. **Settings pages** — registries, credentials, global settings, webhook URL management
|
|
21. **Project config pages** — add/edit/delete projects and stages via UI
|
|
22. **Embed in Go** ✅ — build SvelteKit to static, embed with `go:embed`, serve from Go
|
|
23. **Real-time updates** ✅ — SSE for deploy progress and instance status changes
|
|
|
|
### Phase 4: Volumes & Environment (Phase 13) -- COMPLETED
|
|
|
|
Persistent storage and app-specific configuration for deployed containers.
|
|
|
|
24. **Environment variables per project** — key/value pairs stored in SQLite, sensitive values encrypted
|
|
25. **Per-stage env overrides** — e.g., `NODE_ENV=development` for dev, `NODE_ENV=production` for prod
|
|
26. **Volume mounts per project** — configurable source/target paths with shared/isolated modes
|
|
27. **Shared volumes** — all instances of a project mount the same host path (for stateless apps or shared uploads)
|
|
28. **Isolated volumes** — each instance gets its own subdirectory: `{source}/{stage}-{tag}/` → `{target}` (for stateful apps with local DBs/files)
|
|
29. **UI for volumes & env** — project settings page with key/value editor, volume list, shared/isolated toggle, per-stage override support
|
|
|
|
#### Phase 13 Handoff Notes
|
|
|
|
- New tables: `stage_env` (id, stage_id, key, value, encrypted, timestamps), `volumes` (id, project_id, source, target, mode, timestamps)
|
|
- `stage_env` has UNIQUE(stage_id, key) constraint to prevent duplicate keys per stage
|
|
- Volume mode is either "shared" or "isolated"; default is "shared"
|
|
- Encrypted env values are encrypted with `crypto.Encrypt` before storage and decrypted at deploy time
|
|
- API masks encrypted env values as "••••••••" in responses
|
|
- Env merge order in deployer: project-level JSON `env` field parsed first, then stage-level `stage_env` records overlay (stage wins on key conflict)
|
|
- `computeVolumeMounts` appends `/{stage}-{tag}/` to source for isolated volumes
|
|
- Docker `ContainerConfig` now has `Mounts []mount.Mount` field, passed to `HostConfig.Mounts`
|
|
- Both `executeDeploy` and `blueGreenDeploy` updated to use `mergeEnvVars` and `computeVolumeMounts`
|
|
- API routes: GET/POST `/api/projects/{id}/stages/{stage}/env`, PUT/DELETE `.../env/{envId}`, GET/POST `/api/projects/{id}/volumes`, PUT/DELETE `.../volumes/{volId}`
|
|
- Frontend pages: `/projects/[id]/env` (per-stage env editor with inherited/overridden indicators), `/projects/[id]/volumes` (volume editor with shared/isolated toggle)
|
|
- Project detail page now has navigation links to env and volumes pages
|
|
|
|
Volume config per project:
|
|
```yaml
|
|
env:
|
|
NODE_ENV: production
|
|
DATABASE_URL: postgres://db:5432/myapp # shared external DB
|
|
SECRET_KEY: "..." # encrypted in SQLite
|
|
volumes:
|
|
- source: /data/my-app/uploads
|
|
target: /app/uploads
|
|
mode: shared # all instances share this path
|
|
- source: /data/my-app/data
|
|
target: /app/data
|
|
mode: isolated # auto-appends /{stage}-{tag}/ to source
|
|
```
|
|
|
|
Stage-level env overrides:
|
|
```yaml
|
|
stages:
|
|
dev:
|
|
env:
|
|
NODE_ENV: development # overrides project-level
|
|
DATABASE_URL: postgres://db:5432/myapp_dev
|
|
prod:
|
|
env:
|
|
NODE_ENV: production # uses project-level default
|
|
```
|
|
|
|
### Phase 5: Hardening (Phase 12) -- COMPLETED
|
|
|
|
30. **Blue-green deploys** -- start new, health check, swap, stop old (zero downtime)
|
|
31. **Promote flow** -- enforce `promote_from` for production deploys
|
|
32. **Auth on dashboard** -- two modes, configurable via settings:
|
|
- **Local auth** -- username/password stored in SQLite (bcrypt hashed), JWT session tokens
|
|
- **OAuth2 / OpenID Connect** -- integration with any OIDC provider (configurable client ID/secret/discovery URL)
|
|
33. **Graceful shutdown** -- drain in-progress deploys on SIGTERM, close DB, stop poller
|
|
34. **Structured logging** -- JSON logs via `log/slog` with deploy context
|
|
35. **Config export** -- download current SQLite state as YAML
|
|
36. **Dockerfile** -- multi-stage build (Node.js 20 + Go 1.23 build, alpine runtime)
|
|
37. **docker-compose.yml** -- production-ready compose with volumes, network, env
|
|
38. **Auth middleware** -- protects all /api/* routes except webhook and auth endpoints
|
|
39. **Auth settings UI** -- settings page to toggle auth mode, configure OIDC, manage users
|
|
40. **Login page** -- username/password form with OIDC SSO option
|
|
41. **Final wiring** -- all services properly initialized and shut down in main.go
|
|
|
|
#### Phase 12 Handoff Notes
|
|
|
|
- Auth: `auth.LocalAuth` handles JWT generation/validation, `auth.OIDCProvider` handles OIDC flow
|
|
- Default admin user created on first launch (ADMIN_PASSWORD env var, default: "admin")
|
|
- JWT secret derived from ENCRYPTION_KEY via HMAC-SHA256
|
|
- Blue-green: triggered automatically when stage has `max_instances=1`; otherwise standard deploy
|
|
- Promote: validated in `TriggerDeploy` before deploy begins
|
|
- Graceful shutdown: `deployer.Drain()` waits for in-progress deploys; poller stopped; HTTP server drained; DB closed
|
|
- Structured logging: all API, deployer, and main.go use `log/slog` JSON handler
|
|
- New dependencies: `github.com/golang-jwt/jwt/v5`, `golang.org/x/crypto/bcrypt`, `github.com/coreos/go-oidc/v3`, `golang.org/x/oauth2`
|
|
- New tables: `users` (id, username, password_hash, email, role, timestamps), `auth_settings` (single-row: auth_mode, OIDC config)
|
|
- Auth middleware applied to all `/api/*` routes except `/api/auth/login`, `/api/auth/oidc/*`, `/api/webhook/*`, `/api/config/export`
|
|
- Frontend: token stored in `localStorage`, sent as `Authorization: Bearer` header
|
|
- Run `go mod tidy` after checkout to resolve transitive dependencies
|
|
|
|
## Key Dependencies (Go)
|
|
|
|
- `github.com/docker/docker` — Docker Engine API
|
|
- `github.com/go-chi/chi` or `net/http` — HTTP routing
|
|
- `gopkg.in/yaml.v3` — YAML seed config
|
|
- `modernc.org/sqlite` — SQLite (CGo-free)
|
|
- `github.com/robfig/cron` — Polling scheduler
|
|
- `github.com/google/uuid` — Webhook secret URL generation
|
|
|
|
## Docker Compose (self-deployment)
|
|
|
|
```yaml
|
|
services:
|
|
docker-watcher:
|
|
image: docker-watcher:latest
|
|
container_name: docker-watcher
|
|
restart: unless-stopped
|
|
ports:
|
|
- "8080:8080"
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
- ./docker-watcher.yaml:/app/seed.yaml:ro # optional, first launch only
|
|
- ./data:/app/data # SQLite DB
|
|
environment:
|
|
- ENCRYPTION_KEY=${ENCRYPTION_KEY} # protects all credentials in DB
|
|
networks:
|
|
- staging-net
|
|
|
|
networks:
|
|
staging-net:
|
|
external: true
|
|
```
|
|
|
|
## API Endpoints
|
|
|
|
```text
|
|
# Projects
|
|
GET /api/projects — list all projects with instance counts
|
|
POST /api/projects — create project
|
|
GET /api/projects/:id — project detail + stages + instances
|
|
PUT /api/projects/:id — update project config
|
|
DELETE /api/projects/:id — delete project + all instances
|
|
|
|
# Stages
|
|
POST /api/projects/:id/stages — add stage to project
|
|
PUT /api/projects/:id/stages/:stage — update stage config
|
|
DELETE /api/projects/:id/stages/:stage — delete stage + its instances
|
|
|
|
# Stage Env Overrides
|
|
GET /api/projects/:id/stages/:stage/env — list stage env vars (secrets masked)
|
|
POST /api/projects/:id/stages/:stage/env — create stage env var
|
|
PUT /api/projects/:id/stages/:stage/env/:envId — update stage env var
|
|
DELETE /api/projects/:id/stages/:stage/env/:envId — delete stage env var
|
|
|
|
# Project Volumes
|
|
GET /api/projects/:id/volumes — list project volumes
|
|
POST /api/projects/:id/volumes — create project volume
|
|
PUT /api/projects/:id/volumes/:volId — update project volume
|
|
DELETE /api/projects/:id/volumes/:volId — delete project volume
|
|
|
|
# Instances (running containers)
|
|
GET /api/projects/:id/stages/:stage/instances — list instances for stage
|
|
POST /api/projects/:id/stages/:stage/instances — deploy new instance (pick tag)
|
|
DELETE /api/projects/:id/stages/:stage/instances/:iid — remove instance (container + NPM proxy)
|
|
POST /api/projects/:id/stages/:stage/instances/:iid/stop — stop container
|
|
POST /api/projects/:id/stages/:stage/instances/:iid/start — start stopped container
|
|
POST /api/projects/:id/stages/:stage/instances/:iid/restart — restart container
|
|
|
|
# Quick Deploy
|
|
POST /api/deploy/inspect — pull + inspect image, return defaults
|
|
POST /api/deploy/quick — create project + deploy in one step
|
|
|
|
# Registry
|
|
GET /api/registries — list registries
|
|
POST /api/registries — add registry
|
|
PUT /api/registries/:id — update registry
|
|
DELETE /api/registries/:id — delete registry
|
|
POST /api/registries/:id/test — test connection
|
|
GET /api/registries/:id/tags/:image — list available tags
|
|
|
|
# Settings
|
|
GET /api/settings — get global settings
|
|
PUT /api/settings — update global settings
|
|
GET /api/settings/webhook-url — get secret webhook URL
|
|
POST /api/settings/webhook-url/regenerate — regenerate webhook URL
|
|
|
|
# Deploy history
|
|
GET /api/deploys — recent deploys across all projects
|
|
GET /api/deploys/:id/logs — deploy log stream (SSE)
|
|
|
|
# Webhook (secret URL — no auth needed)
|
|
POST /api/webhook/:secret-uuid — receive image push notification
|
|
```
|
|
|
|
## User Workflows
|
|
|
|
### Auto-Deploy (zero effort)
|
|
|
|
```text
|
|
Push code → CI builds → pushes tag → Docker Watcher detects →
|
|
auto_deploy: true → deployed → notification with URL
|
|
```
|
|
|
|
### Manual Deploy via UI (one click)
|
|
|
|
```text
|
|
Open dashboard → project → stage → "Deploy new version" →
|
|
pick tag from dropdown → click Deploy
|
|
```
|
|
|
|
### Quick Deploy (new project, paste image URL)
|
|
|
|
```text
|
|
Open dashboard → "Quick Deploy" → paste image URL →
|
|
review auto-filled defaults → click Deploy →
|
|
project auto-created + deployed
|
|
```
|
|
|
|
### Deploy via CI Webhook (zero effort after CI setup)
|
|
|
|
```text
|
|
# In .gitea/workflows/build.yml
|
|
- name: Notify Docker Watcher
|
|
run: |
|
|
curl -X POST https://watcher.dolgolyov-family.by/api/webhook/d8f2a1e9-... \
|
|
-d '{"image": "git.dolgolyov-family.by/alexei/my-app:dev-${{ github.sha }}"}'
|
|
```
|
|
|
|
Known project → deploys per stage config.
|
|
Unknown project → auto-creates with defaults from image inspection, deploys.
|
|
|
|
### Production Deploy (two clicks)
|
|
|
|
```text
|
|
Open dashboard → project → prod stage → "Deploy new version" →
|
|
dropdown shows only tags running in "rel" stage (promote_from) →
|
|
pick tag → confirmation dialog → Deploy
|
|
```
|