App Model
App Model
How Pvdify organizes applications.
App Slot
An App Slot is the fundamental deployment unit. Each slot represents one running instance of an application in a specific environment.
Schema
name: myapp # Unique identifierenvironment: production # production | staging | previewimage: ghcr.io/org/app:v1.2.3 # Container imageprocesses: web: command: "npm start" count: 1 worker: command: "npm run worker" count: 1bind_port: 3001 # Internal port (3000+)domains: - myapp.com - www.myapp.comenv_versions: - v1: { created: "2026-01-01", hash: "abc123" } - v2: { created: "2026-01-05", hash: "def456" }resources: memory: 512M # Memory limit cpu: 0.5 # CPU shareshealthcheck: path: /health interval: 30s timeout: 5sFields
| Field | Type | Required | Description |
|---|---|---|---|
name | string | ✓ | Unique app identifier (lowercase, alphanumeric, hyphens) |
environment | enum | ✓ | production, staging, or preview |
image | string | ✓ | Full container image reference |
processes | map | ✓ | Process type definitions |
bind_port | int | ✓ | Internal port (auto-assigned from 3000+) |
domains | []string | Custom domain list | |
env_versions | []object | Config version history | |
resources | object | Memory/CPU limits | |
healthcheck | object | Health check configuration |
Environments
Apps support three environment types:
| Environment | Purpose | Lifecycle |
|---|---|---|
production | Live traffic | Permanent |
staging | Pre-production testing | Permanent |
preview | PR previews | Ephemeral (auto-cleanup) |
Naming Convention
myapp → productionmyapp-staging → stagingmyapp-pr-123 → preview (auto-generated)Processes
Each app can define multiple process types:
processes: web: command: "node server.js" count: 2 # Horizontal scaling worker: command: "node worker.js" count: 1 scheduler: command: "node cron.js" count: 1Process Lifecycle
Each process becomes a systemd unit:
pvdify-myapp-web.servicepvdify-myapp-worker.servicepvdify-myapp-scheduler.serviceManaged via:
pvdify ps myapp # List processespvdify ps:scale web=2 # Scale horizontallypvdify ps:restart web # Restart process typeReleases
A Release is an immutable deployment snapshot:
release: version: 42 image: ghcr.io/org/app:v1.2.3 config_version: v2 created_at: 2026-01-05T10:30:00Z created_by: gh-actions status: activeRelease Lifecycle
- Create — New release created via API/CLI
- Pending — Image pulled, containers prepared
- Deploying — Blue-green swap in progress
- Active — Release is serving traffic
- Superseded — Replaced by newer release (kept for rollback)
Rollback
# View release historypvdify releases myapp
# Rollback to previouspvdify rollback myapp
# Rollback to specific versionpvdify rollback myapp --version 41Config & Secrets
Environment variables stored separately from releases:
# Set configpvdify config:set DATABASE_URL=postgres://...
# Set secret (encrypted with SOPS)pvdify config:set --secret API_KEY=sk_live_...
# View configpvdify config myapp
# Config versioningpvdify config:versions myappConfig Versions
Each config change creates a new version:
env_versions: - version: v1 created: 2026-01-01T10:00:00Z hash: abc123 changes: ["DATABASE_URL"] - version: v2 created: 2026-01-05T10:30:00Z hash: def456 changes: ["API_KEY", "REDIS_URL"]Domains
Custom domains attached to app slots:
# Add domainpvdify domains:add myapp myapp.com
# List domainspvdify domains myapp
# Remove domainpvdify domains:remove myapp myapp.comDomain Resolution
- Domain added to app slot
- Cloudflare DNS record created (via API)
- Cloudflare Tunnel route configured
- TLS handled by Cloudflare (Full strict)
Resources
Memory and CPU limits per process:
resources: memory: 512M # Memory limit (required) cpu: 0.5 # CPU shares (0.0-1.0)Resource Defaults
| Environment | Memory | CPU |
|---|---|---|
| production | 512M | 0.5 |
| staging | 256M | 0.25 |
| preview | 128M | 0.1 |
Health Checks
Liveness probes for process monitoring:
healthcheck: path: /health # HTTP endpoint interval: 30s # Check frequency timeout: 5s # Request timeout retries: 3 # Failures before restartUnhealthy containers are automatically restarted by systemd.
Storage
Apps are stateless by design. For persistence:
| Need | Solution |
|---|---|
| Database | External (PlanetScale, Supabase, Neon) |
| Files | S3 bucket (AWS) |
| Cache | Redis (if needed) |
| Sessions | Cookie-based or Redis |
Example: Full App Definition
# myapp production slotname: myappenvironment: productionimage: ghcr.io/acme/myapp:v2.1.0processes: web: command: "node dist/server.js" count: 2 worker: command: "node dist/worker.js" count: 1bind_port: 3001domains: - myapp.com - www.myapp.com - api.myapp.comenv_versions: - version: v3 created: 2026-01-05T10:30:00Z hash: def456resources: memory: 512M cpu: 0.5healthcheck: path: /health interval: 30s timeout: 5s retries: 3