Docs
API access, CI/CD, exec, persistent storage and operating runner applications.
Init (required before first deploy)
ifhost init generates the impossible.toml config file in your current directory. ifhost deploy errors if it isn't there, forcing you (or your agent) to pick machine specs explicitly rather than relying on silent defaults.
$ ifhost init --app my-site --port 80 --memory 256 $ ifhost init --app my-api --port 3000 --memory 1024 --cpus 2 $ ifhost init --app my-bot --memory 1024 --min-machines 1 --storage local --autostop=false
All flags are optional. Run ifhost init alone for an interactive prompt. Agents should pass full flags to skip prompts entirely.
| Flag | Default | Notes |
|---|---|---|
--app | prompt | App name (becomes <name>.host.impossibuild.ai) |
--port | EXPOSE or 8080 | Auto-detected from Dockerfile if present |
--memory | 256 | RAM in MB: 256, 512, 1024, 2048, 4096 |
--cpus | 1 | 1, 2, 4, 8, capped by your plan |
--cpu-kind | shared | shared anywhere, performance on Pro and Team |
--autostop | false | Opt in only when the app can recover after an idle stop |
--min-machines | 0 | 1 = no cold starts |
--storage | (empty) | local auto-creates a 1 GB /data volume; pins app to one machine |
What your plan allows per machine
| Plan | Max --cpus | CPU kinds |
|---|---|---|
| Free | 1 | shared |
| Hobby | 2 | shared |
| Pro | 4 | shared, performance |
| Team | 8 | shared, performance |
Over the cap, the deploy is rejected and the error names your plan, its cap, and what you asked for. ifhost init warns about it up front, and GET /billing/plans returns the live numbers. RAM works differently: it comes out of one account-wide pool rather than a per-machine cap, so see ifhost billing plan.
impossible.toml directly for changes, or delete it and re-run init. Flags on ifhost deploy (--env, --secret, --port) override the toml for a single deploy.API Access
Every CLI command maps to a REST API call. Use the API directly for automation or building your own tools.
# Base URL https://host.impossibuild.ai # Auth header Authorization: Bearer imp_xxx
Get your token
$ ifhost tokens create --name ci-deploy imp_abc123...
Example: deploy via API
# Create app $ curl -X POST https://host.impossibuild.ai/apps \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "my-app", "region": "sin"}' # Upload source + build + deploy $ curl -X POST "https://host.impossibuild.ai/apps/my-app/source-deploy" \ -H "Authorization: Bearer $TOKEN" \ -F "source=@source.tar.gz" # Check machines $ curl -s "https://host.impossibuild.ai/apps/my-app/machines" \ -H "Authorization: Bearer $TOKEN"
See llms.txt for the full API reference.
CI/CD Integration
GitHub Actions
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Install ifhost
run: |
installer="$(mktemp)"
curl --fail --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 https://host.impossibuild.ai/install --output "$installer"
sh "$installer"
rm -f "$installer"
- name: Deploy
env:
IMPOSSIBLE_API_TOKEN: ${{ secrets.IFHOST_TOKEN }}
run: ~/.local/bin/ifhost deploy --app my-app --yes
GitLab CI
deploy:
script:
- installer="$(mktemp)"
- curl --fail --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 https://host.impossibuild.ai/install --output "$installer"
- sh "$installer"
- rm -f "$installer"
- IMPOSSIBLE_API_TOKEN=$IFHOST_TOKEN ~/.local/bin/ifhost deploy --app my-app --yes
IMPOSSIBLE_API_TOKEN as a secret env var. The CLI reads it automatically - no ifhost login needed.Common CLI Commands
Account (no --app needed)
ifhost login # Interactive sign-in (opens browser) printf '%s' "$IFHOST_TOKEN" | ifhost login --token - # Token from stdin ifhost login --from-file /run/secrets/ifhost-token # Token from a file ifhost login --switch # Switch accounts ifhost logout # Remove active account ifhost status # Plan, apps, spend, CLI version ifhost version # Check for updates ifhost update # Self-update ifhost regions # List regions ifhost tokens create --name x # Create API token ifhost tokens list # List tokens ifhost tokens revoke <id> # Revoke token ifhost billing plans # Plan + limits ifhost billing usage # Usage this period ifhost billing alert set --max 20 # Spend cap
Init (required before first deploy)
ifhost init --app my-app # Interactive - prompts for name ifhost init --app my-app --port 3000 --memory 512 ifhost init --app my-app --cpus 2 --cpu-kind performance --min-machines 1 ifhost init --app my-app --storage local # Auto /data volume ifhost init --app my-app --autostop=false # Always-on (no cold starts)
Deploy
ifhost deploy # Reads impossible.toml from cwd ifhost deploy --region sin # Override region ifhost deploy --port 3000 # Override port ifhost deploy --env NODE_ENV=prod # Env inline ifhost deploy --secret DB_URL=@env:DB_URL # Secret reference ifhost deploy --json # JSON output
Machines (requires --app)
ifhost machines --app my-app # List machines ifhost machines start --app my-app # Start all ifhost machines stop --app my-app # Stop all ifhost machines restart --app my-app # Restart ifhost machines exec --app my-app -- <cmd> # Run command (~60s timeout, first running machine) ifhost machines exec --app my-app --machine <id> -- <cmd> # Target a specific machine ifhost machines console start --app my-app -- bash # Interactive tmux session ifhost machines console input --app my-app <sid> "..." # Send text/keys ifhost machines console output --app my-app <sid> # Capture pane ifhost machines env set K=V --app my-app # Stage env; --restart applies ifhost machines env list --app my-app # List env vars ifhost machines secrets set K=@env:K --app my-app # Stage secret from env ifhost machines secrets list --app my-app # List keys ifhost machines volumes create data --mount /data --size 5 --app my-app ifhost machines domains add api.example.com --app my-app ifhost machines logs --app my-app # Stream logs ifhost machines logs --app my-app --since 1h # Historical ifhost machines destroy --app my-app --yes-irreversible # Delete everything
Exec: Run Commands in Your Container
$ ifhost machines exec --app my-app -- ls /app $ ifhost machines exec --app my-app -- env $ ifhost machines exec --app my-app -- python manage.py migrate $ ifhost machines exec --app my-app -- sh -c "df -h && free -m" # Multi-machine apps - target a specific machine $ ifhost machines --app my-app # list IDs (grouped Running / Standby) $ ifhost machines exec --app my-app --machine 32d41b... -- ps -ef
- ~60s timeout per command - for longer-running setup, use the console below
- No interactive mode (no vim, no stdin) - for that, use the console below
- Machine must be running
- Available tools depend on your base image (Alpine =
sh, nobash) - Without
--machine, exec picks the first running machine. Pin to one to verify a specific replica after a rolling deploy.
Interactive runner setup
For projects whose install needs a wizard, multi-step CLI, or anything you'd normally SSH in to do - there's no Dockerfile yet, the install is interactive, or the project ships a setup command you need to walk through. Boot a generic shell VM, then drive setup through a tmux-backed console.
# 1. Pick machine specs (creates impossible.toml). Storage=local gives a persistent /data. $ ifhost init --app my-app --memory 1024 --min-machines 1 --storage local # 2. Create the app and boot its runner $ ifhost deploy --app my-app # 3. Save credentials from env/files after the app exists $ ifhost machines secrets set OPENAI_API_KEY=@env:OPENAI_API_KEY TG_TOKEN=@file:/secure/tg-token --app my-app # 4. Apply saved configuration before starting the application $ ifhost apply --app my-app # 5. Open a tmux-backed interactive session $ ifhost machines console start --app my-app -- bash # returns { session_id, machine_id } $ SESSION="ifhost-01..." # 6. Drive setup: send commands, send Enter, read the pane $ ifhost machines console input --app my-app $SESSION "<command>" $ ifhost machines console input --app my-app $SESSION --key Enter $ ifhost machines console output --app my-app $SESSION --lines 80
tmux is auto-installed on the first console start if missing - works on Debian/Ubuntu (apt) and Alpine (apk) bases.
Long-running daemons
If the project starts a daemon (gateway, message-bus loop, watcher), launch it inside a named detached tmux session inside the container so it survives the console disconnect:
$ ifhost machines console input --app my-app $SESSION \
"tmux new-session -d -s app 'cd /data/src && exec ./run 2>&1 | tee /data/app.log'"
For Python piped output, always set PYTHONUNBUFFERED=1 - otherwise log files stay empty for minutes due to block-buffering.
Polling for completion
Long commands (apt installs, npm, pip with native compiles) exceed the 60s exec timeout. The pattern: emit a unique marker after each command and poll the console output until it lands.
$ ifhost machines console input --app my-app $SESSION \ "apt-get install -y X Y Z; echo __DONE__rc=\$?" $ ifhost machines console input --app my-app $SESSION --key Enter # Poll every ~10s until the marker shows up: $ ifhost --json machines console output --app my-app $SESSION \ | jq -r .output | grep __DONE__rc=
Persistent runner storage
Set storage = "local" in impossible.toml so the project's /data volume survives machine restarts. Then point the project's data directory at /data via env var (HERMES_HOME=/data, XDG_DATA_HOME=/data, etc).
ifhost deploy uses this same workflow.Runner capacity
Runner apps use one machine. Manual replica scaling and autoscale commands are not available in the current CLI. A persistent volume attaches to that one machine.
To change CPU or memory, edit [resources] in impossible.toml, then run ifhost apply from that project directory. The account plan and available pool limit the requested resources.
$ ifhost apply $ ifhost machines --app my-app
Keep [service] autostop = false and min_machines = 1 for a manually started application unless it is prepared to recover through the stop/start lifecycle. A started machine alone does not prove that the application is serving requests.
Persistent Volumes
Two ways to get a volume on first deploy - both auto-attach.
Shorthand: set storage = "local" in impossible.toml and a 1 GB volume named data is auto-created at /data on first deploy.
# impossible.toml
app = "my-app"
storage = "local"
Explicit: declare one or more [[volumes]] blocks with custom name/size/mount path. Auto-created on first deploy if absent.
# impossible.toml
[[volumes]]
name = "mydata"
size_gb = 5
mount_path = "/data"
Manual: create out-of-band before deploy.
$ ifhost machines volumes create mydata --mount /data --size 5 --app my-app $ ifhost deploy --app my-app # Volume auto-attaches
Volumes are region-pinned block storage: a directory on a fast SSD attached to one machine. They're for embedded state (SQLite, file caches, generated artifacts). They disable horizontal auto-scaling - apps with a volume run on a single machine. Need a database that scales? Use a managed service (Supabase, Neon, Upstash, Turso) and skip storage = "local" entirely.
Spend Caps
$ ifhost billing alert set --max 20 # Warn at $20/mo $ ifhost billing alert # Check cap + current spend $ ifhost billing alert off # Remove
JSON Output
# App list $ ifhost status --json | jq '.apps[].name' # Deploy + capture URL $ URL=$(ifhost deploy --app x --json | jq -r '.url') # Machine details $ ifhost machines --app x --json | jq '.machines[] | {id, state, region}'
Config File Reference
ifhost deploy requires impossible.toml in the current project directory. Run ifhost init to create it, then edit the settings for your app. Supported command flags override the corresponding file settings for that invocation.
app = "my-app" region = "sin" [service] internal_port = 3000 autostop = false min_machines = 1 [resources] cpu_kind = "shared" # "shared" or "performance" (performance on Pro and Team) cpus = 1 # 1, 2, 4, 8 (your plan caps the max) memory_mb = 512 # 256, 512, 1024, 2048, ... [[volumes]] name = "data" size_gb = 5 mount_path = "/data" [env] NODE_ENV = "production" # Non-sensitive only
Regions
| Code | Location | Code | Location |
|---|---|---|---|
| iad | Virginia, US | lhr | London, UK |
| ewr | New Jersey, US | fra | Frankfurt, DE |
| ord | Chicago, US | ams | Amsterdam, NL |
| dfw | Dallas, US | cdg | Paris, FR |
| lax | Los Angeles, US | arn | Stockholm, SE |
| sjc | San Jose, US | nrt | Tokyo, JP |
| yyz | Toronto, CA | sin | Singapore |
| gru | Sao Paulo, BR | syd | Sydney, AU |
| jnb | Johannesburg, ZA | bom | Mumbai, IN |
Set with ifhost deploy --region sin or region = "sin" in impossible.toml.
Multi-Account
$ ifhost login # Add account 1 $ ifhost login # Add account 2 $ ifhost login --switch # Pick by number $ ifhost logout # Remove active # CI override (ignores saved profiles) $ IMPOSSIBLE_API_TOKEN="$IFHOST_TOKEN" ifhost deploy --app x
Troubleshooting
Machine started but the app URL fails
Confirm that you uploaded the project, installed its dependencies and started its server. Check its logs, its bind address and the configured internal port.
Application files are missing
Deploy does not upload source. Run ifhost machines push --app my-app . --to /app, inspect the destination with exec, and run the project setup commands there.
Machine won't start
Check logs: ifhost machines logs --app my-app --since 30m. Common causes: wrong port, missing env var, crash on startup.
"app name already taken"
Names are globally unique. Pick a different one.
Env/secret change didn't take effect
Both env set and secrets set stage changes by default. API writes and deletes also save without restarting; add ?restart=true to apply immediately. Run ifhost machines restart --app x after all changes, or pass --restart on the last command.
CLI out of date
$ ifhost version # Check $ ifhost update # Update