Common Deployment Patterns
Reference for the most frequently used cpctl workflows, from a single image deploy to a multi-service application with custom domains, autoscaling, and CI/CD.
1. First-time setup
bash
# Log in with your API token from computeportal.io/user/compute
cpctl login --token <your-api-token>
# Verify identity
cpctl whoami
# → email: you@example.com
# → user_id: usr_abc123
# Check your compute quota before deploying
cpctl quota
# Plan: basic-1-2-50 (basic)
#
# ┌──────────┬────────┬──────────┬─────┐
# │ RESOURCE │ USED │ LIMIT │ PCT │
# ├──────────┼────────┼──────────┼─────┤
# │ CPU │ 0m │ 1000m │ 0% │
# │ Memory │ 0 MiB │ 2048 MiB │ 0% │
# │ Storage │ — │ 50 GiB │ — │
# └──────────┴────────┴──────────┴─────┘2. Deploy a public Docker image
The simplest case — pull a published image and expose it.
bash
cpctl deploy --image nginx:latest --name my-nginx
# → Deploying nginx:latest...
# ✓ Deployed my-nginx
# service my-nginx
# url https://my-nginx-<hash>.cpctl.app
# status runningWait for the service to be healthy before returning:
bash
cpctl deploy --image nginx:latest --name my-nginx --wait --timeout 1203. Deploy from a GitHub repository
Compute Portal clones, builds, and runs the repo.
bash
# Public repo
cpctl deploy --repo https://github.com/your-org/your-app
# Private repo — GitHub must be connected at computeportal.io/user/compute?tab=cli
# cpctl auto-detects the private repo and injects a short-lived token
cpctl deploy --repo https://github.com/your-org/your-app --name my-app
# Target a specific branch
cpctl deploy --repo https://github.com/your-org/your-app --branch staging --name my-app-stagingBuild logs stream live during the build:
Building my-app — job: cp-build-my-app-...
[INFO] RUN npm install
[INFO] RUN npm run build
[INFO] CMD ["npm run start"]
[INFO] Pushed registry.example.com/cp-cli/my-app:latest
→ Build complete. Service available at: https://my-app-<hash>.cpctl.app4. Deploy a monorepo subpath
When only one service lives under a subdirectory:
bash
# Build from ./backend inside the repo
cpctl deploy \
--repo https://github.com/your-org/your-monorepo \
--subpath backend \
--name backend-api
# Frontend in the same repo, separate service
cpctl deploy \
--repo https://github.com/your-org/your-monorepo \
--subpath frontend \
--name frontend-app5. Deploy with environment variables
Pass secrets and config at deploy time, or set them separately after.
bash
# Inline at deploy time
cpctl deploy \
--image your-org/my-app:latest \
--name my-app \
--env-var DATABASE_URL=postgres://host/db \
--env-var SECRET_KEY=<your-secret> \
--env-var NODE_ENV=production
# Set individual variables
cpctl env set my-app \
DATABASE_URL=postgres://host/db \
REDIS_URL=redis://cache:6379
# Verify what is set
cpctl env list my-app
# → DATABASE_URL postgres://host/db
# → REDIS_URL redis://cache:6379
# → NODE_ENV production
# Remove a variable
cpctl env unset my-app SECRET_KEYIf a .env.example exists in the repo, cpctl warns about unset keys after deploy:
⚠ .env.example found — 2 variable(s) may be required:
STRIPE_SECRET_KEY=<value>
SENDGRID_API_KEY=<value>
Set them with:
cpctl env set my-app \
STRIPE_SECRET_KEY=<value> \
SENDGRID_API_KEY=<value>6. Deploy with a specific port
Use when the container binds to a known fixed port.
bash
cpctl deploy --image your-org/api:latest --name api --port 8080
# If port is not specified, cpctl tries to detect it from the image EXPOSE directive.
# If neither works, PORT is injected as an env var and the app must read it.7. Scale a running service
bash
# Manual replica scaling
cpctl scale my-app --replicas 3
# Configure horizontal pod autoscaling (HPA)
cpctl scale my-app --min 2 --max 10 --cpu-threshold 70
# HPA with memory threshold too
cpctl scale my-app --min 2 --max 20 --cpu-threshold 60 --memory-threshold 80
# Turn autoscaling off (back to manual replicas)
cpctl scale my-app --autoscale off
# Check current status after scaling
cpctl status my-app
# → name my-app
# → status running
# → replicas 3 / 3
# → desired_replicas 38. Deploy a GPU workload
Note: GPU nodes are available on select plans. See computeportal.io/pricing. GPU-specific CLI flags (
--gpu) are in preview.
GPU deploys target nodes with the NVIDIA device plugin and assign 1 GPU per replica.
bash
# RTX 4090
cpctl deploy \
--image your-org/inference:latest \
--name inference-api \
--gpu rtx4090
# RTX 5090 for larger model inference
cpctl deploy \
--image your-org/llm:latest \
--name llm-service \
--gpu rtx5090
# Check GPU nodes available in the cluster
cpctl gpu list
# Monitor the GPU service
cpctl status llm-service
# → gpu rtx5090
# → cpu 4
# → memory 16Gi9. Attach a custom domain
After deploying, point your domain's CNAME to the platform and attach it.
bash
# Add the CNAME at your DNS provider first (DNS only, not proxied):
# api.example.com → cpctl.app
# Attach the domain (TLS provisioned automatically via HTTP-01)
cpctl domain add my-app api.example.com
# ✓ Domain api.example.com attached to my-app
# domain api.example.com
# service my-app
# status provisioning
# tls active
# Check TLS provisioning status (may take a few minutes)
cpctl domain status my-app api.example.com
# status active
# tls active
# cert_verified yes
# List all domains on a service
cpctl domain list my-app10. IP firewall rules
Block or restrict access to a service by IP or CIDR.
bash
# Block a specific IP
cpctl firewall add my-app --deny 203.0.113.45
# Block an entire range
cpctl firewall add my-app --deny 198.51.100.0/24
# Allow-list: only this CIDR can reach the service (everyone else blocked)
cpctl firewall add my-app --allow 10.0.0.0/8
# List current rules
cpctl firewall list my-app
# ┌──────────────────────────┬──────┬────────────────────┐
# │ RULE │ MODE │ CIDR │
# ├──────────────────────────┼──────┼────────────────────┤
# │ deny:203.0.113.45/32 │ deny │ 203.0.113.45/32 │
# │ deny:198.51.100.0/24 │ deny │ 198.51.100.0/24 │
# └──────────────────────────┴──────┴────────────────────┘
# Remove a single rule by its ID
cpctl firewall remove my-app deny:203.0.113.45/32
# Disable firewall entirely (remove all rules)
cpctl firewall off my-app11. View logs and connect to a running container
bash
# Stream live logs
cpctl logs my-app
# Tail last 100 lines
cpctl logs my-app --tail 100
# Execute a command inside the running container
cpctl exec my-app -- ls /app
# Open an interactive shell
cpctl exec my-app -- /bin/sh
# SSH into the pod (if SSH server is running in the image)
cpctl ssh my-app12. Service lifecycle
bash
# Stop a running service (scales to 0 replicas, preserves config)
cpctl stop my-app
# Start it again (returns to 1 replica)
cpctl start my-app
# Restart without downtime
cpctl machine restart my-app
# List all services
cpctl machine list
# ┌──────────┬────────────────────┬─────────┬─────────┬─────────────────────────────────────────────────┐
# │ NAME │ IMAGE │ STATUS │ REGION │ URL │
# ├──────────┼────────────────────┼─────────┼─────────┼─────────────────────────────────────────────────┤
# │ my-app │ your-org/my-app │ running │ eu-west │ https://my-app-<hash>.cpctl.app │
# │ my-nginx │ nginx:latest │ stopped │ eu-west │ https://my-nginx-<hash>.cpctl.app │
# └──────────┴────────────────────┴─────────┴─────────┴─────────────────────────────────────────────────┘
# Permanently delete service and all associated resources
cpctl delete my-app
# → Are you sure you want to delete my-app? [y/N] y
# → ✓ Deleted my-app13. Redeploy and rollback
bash
# Redeploy with a new image tag (zero-downtime rolling update)
cpctl deploy --image your-org/my-app:v2.1.0 --name my-app
# View deployment history
cpctl deployment list my-app
# ┌──────────┬─────────┬───────────────────────────┬─────────────────────┐
# │ ID │ STATUS │ IMAGE │ DEPLOYED AT │
# ├──────────┼─────────┼───────────────────────────┼─────────────────────┤
# │ dep_abc │ success │ your-org/my-app:v2.1.0 │ 2026-08-27T21:00:00 │
# │ dep_xyz │ success │ your-org/my-app:v2.0.0 │ 2026-08-20T14:30:00 │
# └──────────┴─────────┴───────────────────────────┴─────────────────────┘
# Roll back to the previous deploy
cpctl rollback my-app
# Or roll back to a specific deployment ID
cpctl rollback my-app dep_xyz14. Multi-service application (cp.json)
Commit a cp.json to the repo root and run cpctl deploy with no flags.
json
{
"project": "my-api",
"image": "your-org/my-api:latest",
"region": "eu-west",
"env": {
"NODE_ENV": "production",
"PORT": "3000"
},
"domains": ["api.example.com"]
}bash
# Reads cp.json — no flags needed
cpctl deploy
# → Deploying your-org/my-api:latest...
# → ✓ Deployed my-api15. Preview environments for pull requests
bash
# Create a PR preview environment (isolated, ephemeral)
cpctl preview create my-app --branch feature/new-auth
# ✓ Preview created
# url https://my-app-pr-42-<hash>.cpctl.app
# List active previews
cpctl preview list my-app
# Tear down after merge
cpctl preview delete my-app --branch feature/new-auth16. Monitor quota before and after deploy
bash
# Check headroom before deploying
cpctl quota
# Plan: basic-1-2-50 (basic)
#
# ┌──────────┬──────────┬──────────┬─────┐
# │ RESOURCE │ USED │ LIMIT │ PCT │
# ├──────────┼──────────┼──────────┼─────┤
# │ CPU │ 900m │ 1000m │ 90% │
# │ Memory │ 1152 MiB │ 2048 MiB │ 56% │
# │ Storage │ — │ 50 GiB │ — │
# └──────────┴──────────┴──────────┴─────┘
# ⚠ You are using over 80% of your plan quota. Consider upgrading at computeportal.io/pricing
# If you try to exceed quota, deploy is blocked:
cpctl deploy --image nginx:latest --name my-service
# → Error: CPU quota exceeded: need 100m, have 100m available
# → Hint: Upgrade your plan or stop another service
# Stop an existing service to free up room, then retry
cpctl stop old-service
cpctl deploy --image nginx:latest --name my-service --wait17. Region selection
bash
# Deploy to a specific region
cpctl deploy --image your-org/api:latest --name eu-api --region eu-west
cpctl deploy --image your-org/api:latest --name us-api --region us-east
# Pin all subsequent commands to a region
cpctl --region eu-west machine list
# List available regions
cpctl region list18. Metrics and billing
bash
# Live resource usage for all services
cpctl metrics
# Usage for a specific service
cpctl metrics my-app
# Current compute quota usage
cpctl quota
# Billing history (last 30 days)
cpctl billing history
# Current balance and burn rate
cpctl billing balance