Skip to content

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   running

Wait for the service to be healthy before returning:

bash
cpctl deploy --image nginx:latest --name my-nginx --wait --timeout 120

3. 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-staging

Build 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.app

4. 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-app

5. 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_KEY

If 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  3

8. 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 16Gi

9. 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-app

10. 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-app

11. 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-app

12. 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-app

13. 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_xyz

14. 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-api

15. 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-auth

16. 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 --wait

17. 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 list

18. 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

GPU Compute Platform