Docs

CLI Guide Advanced

API access, CI/CD, exec, auto-scaling, and everything else for power users.

Contents

Init (required) API Access CI/CD Integration All CLI Commands Exec (run commands in container) Interactive setup (--runner + console) Auto-Scaling Persistent Volumes Spend Caps JSON Output Config File Reference Regions Multi-Account Troubleshooting

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.

FlagDefaultNotes
--apppromptApp name (becomes <name>.fly.dev)
--portEXPOSE or 8080Auto-detected from Dockerfile if present
--memory256RAM in MB: 256, 512, 1024, 2048, 4096
--cpus11, 2, 4, 8
--cpu-kindsharedshared or performance
--cmd(Dockerfile)Startup command override
--autostoptrueSet false for slow-booting apps (ML, Node monoliths)
--min-machines01 = no cold starts
--storage(empty)local auto-creates a 3 GB /data volume; pins app to one machine
After init: edit 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://impossible-api.fly.dev

# 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://impossible-api.fly.dev/apps \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"name": "my-app", "region": "sin"}'

# Upload source + build + deploy
$ curl -X POST "https://impossible-api.fly.dev/apps/my-app/source-deploy" \
    -H "Authorization: Bearer $TOKEN" \
    -F "source=@source.tar.gz"

# Check machines
$ curl -s "https://impossible-api.fly.dev/apps/my-app/machines" \
    -H "Authorization: Bearer $TOKEN"

See llm.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@v4

      - name: Install ifhost
        run: curl -fsSL https://impossible-api.fly.dev/install | sh

      - name: Deploy
        env:
          IMPOSSIBLE_API_TOKEN: ${{ secrets.IFHOST_TOKEN }}
        run: ~/.local/bin/ifhost deploy --app my-app --yes

GitLab CI

deploy:
  script:
    - curl -fsSL https://impossible-api.fly.dev/install | sh
    - IMPOSSIBLE_API_TOKEN=$IFHOST_TOKEN ~/.local/bin/ifhost deploy --app my-app --yes
Any CI: Set IMPOSSIBLE_API_TOKEN as a secret env var. The CLI reads it automatically — no ifhost login needed.

All CLI Commands

Account (no --app needed)

ifhost login                         # GitHub OAuth
ifhost login --token imp_xxx         # Token auth (CI)
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 plan                  # 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=xxx                # Secret inline
ifhost deploy --local                            # Force local Docker
ifhost deploy --remote                           # Force remote source upload
ifhost deploy --runner                           # Generic shell runner (no Dockerfile)
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 scale 3 --app my-app             # Scale to 3
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         # Set env (auto-restart)
ifhost machines env list --app my-app            # List env vars
ifhost machines secrets set K=V --app my-app     # Set secret
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 autoscale set --min 1 --max 5 --target 25 --app my-app
ifhost machines destroy --app my-app --yes       # 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

Interactive setup (--runner + console)

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. Inject any credentials the project will need
$ ifhost machines secrets set OPENAI_API_KEY=... TG_TOKEN=... --app my-app

# 3. Boot a generic shell runner — no Dockerfile, no build
$ ifhost deploy --runner --app my-app

# 4. Re-deploy --runner once after secrets — re-applies the runner init
$ ifhost deploy --runner --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 ""
$ 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 storage with --runner

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

Constraints. --runner cannot be combined with --cmd, --local, or --remote. It boots a generic shell — production HTTP services should still ship as a Dockerfile via plain ifhost deploy.

Auto-Scaling

Default: 1 machine, scale-to-zero (stops when idle, wakes on request).

# Enable: keep 1 always on, scale to 5 under load
$ ifhost machines autoscale set --min 1 --max 5 --target 25 --app my-app

# Disable
$ ifhost machines autoscale off --app my-app

--min = always-on machines. --max = ceiling. --target = concurrent requests per machine before scaling up.

Manual vs auto: ifhost machines scale 3 is immediate but won't auto-adjust. autoscale set lets the platform manage it based on traffic (takes effect on next deploy).
Rolling deploys: ifhost deploy on a multi-machine app updates the primary first, then rolls the new image out to every remaining machine (running and standby). A deploy is only "done" once all machines run the new image — use ifhost machines --app <name> to check, and ifhost machines exec --machine <id> to verify a specific replica.

Persistent Volumes

Two ways to get a volume on first deploy — both auto-attach.

Shorthand: set storage = "local" in impossible.toml and a 3 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

impossible.toml at project root. Optional — saves you from passing flags every time.

app = "my-app"
region = "sin"

[build]
  dockerfile = "Dockerfile"

[service]
  internal_port = 3000
  autostop = true
  min_machines = 0

[resources]
  cpu_kind = "shared"              # "shared" or "performance"
  cpus = 1                         # 1, 2, 4, 8, 16
  memory_mb = 512                  # 256, 512, 1024, 2048, ...

[autoscale]
  min = 1
  max = 5
  concurrency_target = 25

[[volumes]]
  name = "data"
  size_gb = 5
  mount_path = "/data"

[env]
  NODE_ENV = "production"          # Non-sensitive only

Regions

CodeLocationCodeLocation
iadVirginia, USlhrLondon, UK
ewrNew Jersey, USfraFrankfurt, DE
ordChicago, USamsAmsterdam, NL
dfwDallas, UScdgParis, FR
laxLos Angeles, USarnStockholm, SE
sjcSan Jose, USnrtTokyo, JP
yyzToronto, CAsinSingapore
gruSao Paulo, BRsydSydney, AU
jnbJohannesburg, ZAbomMumbai, 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=imp_xxx ifhost deploy --app x

Troubleshooting

"no Dockerfile found"

Your project root needs a Dockerfile. Write one or ask your AI agent to generate it.

Deploy hangs at "Uploading source"

Source is too large (max 100MB). Add a .dockerignore to skip node_modules, .git, build artifacts.

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 auto-restart the machine. If it's stopped, start first: ifhost machines start --app x.

CLI out of date

$ ifhost version     # Check
$ ifhost update      # Update