Portacode CI/CD Intro
Portacode runs CI/CD-style workflows from a simple YAML file. A workflow can do two powerful things:
If you want to run these workflows on your own infrastructure, first set up a Proxmox node: Set Up Proxmox As A Portacode Infra Node.
1 Deploy In A Clean Disposable Environment
Deploy something (or run tests/builds) in a clean, disposable environment. Portacode can provision a fresh managed container/server, run your steps (setup -> build -> test -> deploy), expose ports if needed, then notify you and optionally delete the environment on success.
2 Run Against An Existing Server
Run the same kind of steps on a server you already have. Useful when you want to deploy, test, or run a runbook against an existing staging/production device - still driven by the same YAML steps and the same success/failure behavior.
What Is portafile.yaml?
portafile.yaml is a YAML config that Portacode can load from:
/dashboard/?portafile=<URL-TO-YAML>
It can prefill:
- template selection
- device name / hostname
- resource sizing
- project paths
- automation behavior (
instructions,expose_ports,on_success,on_failure)
Two Ways To Run The YAML
1) One-Click Template (new device)
Use YAML with source_template to provision a new container/device, then optionally run automation instructions.
Browse ready-made templates here: One-Click Deployment Templates.
2) Existing Device AutomationTask
Use YAML without source_template to run steps on an already existing device.
This is useful for CI/CD jobs like test/build/deploy pipelines.
One-Click Template Params
| Param | Type / Allowed input | Default | Short description + example values |
|---|---|---|---|
source_template |
string template selector |
first available template on target node | Picks the base container template for one-click provisioning. See source_template matching rules for exact selector formats and examples. |
name |
string |
"Alpine" |
Device name for created server. Example: "test-runner-01" |
hostname |
string |
"alpine" |
Hostname for created server. Example: "ci-runner" |
username |
string |
"root" |
Username for the created server. Example: "deploy" |
project_paths |
array<string>, max 10 |
[] |
Project folders to create. Supports ~ and env vars like $HOME. Example: ["~/app", "$HOME/tests"] |
inputs |
array<object> |
[] |
Optional input definitions rendered as form fields before deploy / run. Example: [{ id: "telegram_bot_token", type: "secret", label: "Telegram bot token" }] |
inputs[].id |
string matching ^[A-Za-z][A-Za-z0-9_]*$ |
N/A (required per input) | Local input id used inside automation placeholders like ${inputs.telegram_bot_token}. Example: "telegram_bot_token" |
inputs[].type |
text | textarea | secret | number | boolean | email | url | select | multiselect | color | file |
"text" |
Controls which form field Portacode renders automatically. Example: "secret" |
inputs[].save |
string key or false |
local input id | Controls whether the field can be saved to the user's account for reuse. Use a string key like "openclaw.telegram.bot_token" for a reusable preset key, or false to hide the save toggle. |
resources.disk_gib |
positive integer | 3 |
Requested disk size in GiB. Example: 8 |
resources.ram_mib |
positive integer | 1024 |
Requested RAM in MiB. Example: 2048 |
resources.cpus |
decimal > 0 |
0.1 |
Requested CPU share. Example: 1, 2 |
AutomationTask Params
Note: these params are also supported in one-click templates.
| Param | Type / Allowed input | Default | Short description + example values |
|---|---|---|---|
instructions |
array of steps. Each step can be a string command or object with run | cmd | command | wait_for |
N/A (required) | Ordered automation steps. wait_for polls an HTTP(S) URL until it returns 2xx; supports placeholders like https://[exposed:3000]/health (polls every 3s, request timeout 5s, default step timeout 600s, overridable via step timeout). |
inputs |
array<object> |
[] |
Optional runtime input definitions rendered live from YAML in the automation form. Values are stored separately from instructions and sent alongside the task. |
inputs[].id |
string matching ^[A-Za-z][A-Za-z0-9_]*$ |
N/A (required per input) | Local input id referenced from steps and metadata using ${inputs.<id>}. Example: ${inputs.site_name} |
inputs[].type |
text | textarea | secret | number | boolean | email | url | select | multiselect | color | file |
"text" |
Determines the rendered control and normalization rules for submitted values. |
inputs[].save |
string key or false |
local input id | Enables the optional "save to my account" toggle. A string sets the reusable saved-input key; false disables saving for that field. |
task_name |
string |
"Automation Task" |
Human-readable task name used in notifications. Example: "Nightly Test Run" |
expose_ports |
array<int | object> where each entry is either a port number or { port: <int>, protocol: http|https }, max 3 |
[] |
Ports to expose before steps run. Bare integers default to HTTP origins. Example: [3000, { port: 8443, protocol: https }] |
on_success |
enum: notify | notify_and_delete | do_nothing |
notify |
What to do after success. Example: notify_and_delete |
on_failure |
enum: notify | suggest_fixes | auto_fix | retry OR object { retry: <int> } |
notify |
What to do after failure. Examples: suggest_fixes, retry, { retry: 3 } |
For HTTPS/custom-domain exposure via expose_ports, also configure Cloudflare tunnel on that device:
Connect A Device To A Domain (Cloudflare Tunnel).
source_template Matching Rules
source_template is matched against the template catalog discovered from the target Proxmox node. This is intentionally dynamic, so values differ between infrastructures.
Supported selector forms:
- Exact template ID:
local:vztmpl/ubuntu-24.04-standard_24.04-2_amd64.tar.zst - Short name / stem / family prefix:
ubuntu,ubuntu-24.04,alpine,debian-12 - Version constraints:
- Prefix operator form:
<=ubuntu-24.04,>debian-11,=alpine-3.19 - Infix form:
ubuntu<=24.04,debian>11
- Prefix operator form:
- Comma-separated fallbacks (first match wins):
ubuntu-24.04, ubuntu-22.04, alpine
If no selector is provided, the UI uses the default selected template (derived from available templates for that node).
Examples
One-click template + automation
source_template: ubuntu<=24.04, ubuntu-22.04, alpine
name: test-runner
hostname: test-runner
username: deploy
resources:
disk_gib: 8
ram_mib: 2048
cpus: 1
project_paths:
- ~/app
automation_task:
instructions:
- run: npm run build
expose_ports:
- 3000
- port: 8443
protocol: https
Existing device AutomationTask
task_name: Smoke Test + Public Readiness
instructions:
- run: pytest -q
- wait_for: https://[exposed:8080]/
expose_ports:
- 8080
- port: 8443
protocol: https
on_success: notify_and_delete
on_failure:
retry: 3
Add A Deploy Button To Your GitHub README
[Deploy with Portacode](https://portacode.com/dashboard/?portafile=https%3A%2F%2Fraw.githubusercontent.com%2FOWNER%2FREPO%2Fmain%2Fportafile.yaml)
Replace OWNER, REPO, and branch as needed.