Sign up

Docs

API access, CI/CD, exec, persistent storage and operating runner applications.

Init (required before first deploy)

innstance init generates the impossible.toml config file in your current directory. innstance deploy errors if it isn't there, forcing you (or your agent) to pick machine specs explicitly rather than relying on silent defaults.

$ innstance init --app my-site --port 80 --memory 256
$ innstance init --app my-api --port 3000 --memory 1024 --cpus 2
$ innstance init --app my-bot --memory 1024 --min-machines 1 --storage local --autostop=false

All flags are optional. Run innstance init alone for an interactive prompt. Agents should pass full flags to skip prompts entirely.

FlagDefaultNotes
--apppromptApp name. Use the public URL returned after deployment.
--portEXPOSE or 8080Auto-detected from Dockerfile if present
--memory256RAM in MB: 256, 512, 1024, 2048, 4096
--cpus11, 2, 4, 8, capped by your plan
--cpu-kindsharedshared anywhere, performance on Pro and Team
--autostopfalseOpt in only when the app can recover after an idle stop
--min-machines01 = no cold starts
--storage(empty)local auto-creates a 1 GB /data volume; pins app to one machine

What your plan allows per machine

PlanMax --cpusCPU kinds
Free1shared
Hobby2shared
Pro4shared, performance
Team8shared, performance

Over the cap, the deploy is rejected and the error names your plan, its cap, and what you asked for. innstance 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 innstance billing plan.

After init: edit impossible.toml directly for changes, or delete it and re-run init. Flags on innstance 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://innstance.impossibuild.ai

# Auth header
Authorization: Bearer imp_xxx

Get your token

$ innstance tokens create --name ci-deploy
imp_abc123...

Example: provision a runner via API

# Create app
$ curl -X POST https://innstance.impossibuild.ai/apps \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"name": "my-app", "region": "sin"}'

# Provision the runner
$ curl -X POST "https://innstance.impossibuild.ai/apps/my-app/runner-deploy" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{}'

# Check machines
$ curl -s "https://innstance.impossibuild.ai/apps/my-app/machines" \
    -H "Authorization: Bearer $TOKEN"

This boots the runner. Upload your files, install dependencies and start your process using the deployment steps, then check its HTTP response. Provisioning alone does not publish your application.

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 the Innstance CLI
        run: |
          installer="$(mktemp)"
          curl --fail --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 https://innstance.impossibuild.ai/install --output "$installer"
          sh "$installer"
          rm -f "$installer"

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

GitLab CI

deploy:
  script:
    - installer="$(mktemp)"
    - curl --fail --show-error --location --proto '=https' --proto-redir '=https' --tlsv1.2 https://innstance.impossibuild.ai/install --output "$installer"
    - sh "$installer"
    - rm -f "$installer"
    - IMPOSSIBLE_API_TOKEN=$IFHOST_TOKEN ~/.local/bin/innstance deploy --app my-app --yes
Any CI: Set IMPOSSIBLE_API_TOKEN as a secret env var. The CLI reads it automatically - no innstance login needed.

Common CLI Commands

Account (no --app needed)

innstance login                         # Interactive sign-in (opens browser)
printf '%s' "$IFHOST_TOKEN" | innstance login --token - # Token from stdin
innstance login --from-file /run/secrets/innstance-token # Token from a file
innstance login --switch                # Switch accounts
innstance logout                        # Remove active account
innstance status                        # Plan, apps, spend, CLI version
innstance version                       # Check for updates
innstance update                        # Self-update
innstance regions                       # List regions
innstance tokens create --name x        # Create API token
innstance tokens list                   # List tokens
innstance tokens revoke <id>            # Revoke token
innstance billing plans                  # Plan + limits
innstance billing usage                 # Usage this period
innstance billing alert set --max 20    # Spend alert

Init (required before first deploy)

innstance init --app my-app                         # Interactive - prompts for name
innstance init --app my-app --port 3000 --memory 512
innstance init --app my-app --cpus 2 --cpu-kind performance --min-machines 1
innstance init --app my-app --storage local         # Auto /data volume
innstance init --app my-app --autostop=false        # Always-on (no cold starts)

Deploy

innstance deploy                                    # Reads impossible.toml from cwd
innstance deploy --region sin                       # Override region
innstance deploy --port 3000                        # Override port
innstance deploy --env NODE_ENV=prod                # Env inline
innstance deploy --secret DB_URL=@env:DB_URL        # Secret reference
innstance deploy --json                             # JSON output

Machines (requires --app)

innstance machines --app my-app                     # List machines
innstance machines start --app my-app               # Start all
innstance machines stop --app my-app                # Stop all
innstance machines restart --app my-app             # Restart
innstance machines exec --app my-app -- <cmd>       # Run non-interactive command on first running machine
innstance machines exec --app my-app --machine <id> -- <cmd>   # Target a specific machine
innstance machines console start --app my-app -- bash   # Interactive tmux session
innstance machines console input --app my-app <sid> "..."  # Send text/keys
innstance machines console output --app my-app <sid>       # Capture pane
innstance machines env set K=V --app my-app         # Stage env; --restart applies
innstance machines env list --app my-app            # List env vars
innstance machines secrets set K=@env:K --app my-app # Stage secret from env
innstance machines secrets list --app my-app        # List keys
innstance machines volumes create data --mount /data --size 5 --app my-app
innstance machines domains add api.example.com --app my-app
innstance machines logs --app my-app                # Stream logs
innstance machines logs --app my-app --since 1h     # Historical
innstance machines destroy --app my-app --yes-irreversible       # Delete everything

Exec: Run Commands in Your Container

$ innstance machines exec --app my-app -- ls /app
$ innstance machines exec --app my-app -- env
$ innstance machines exec --app my-app -- python manage.py migrate
$ innstance machines exec --app my-app -- sh -c "df -h && free -m"

# Multi-machine apps - target a specific machine
$ innstance machines --app my-app                    # list IDs (grouped Running / Standby)
$ innstance machines exec --app my-app --machine 32d41b... -- ps -ef
  • For long-running or interactive 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, no bash)
  • 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.
$ innstance init --app my-app --memory 1024 --min-machines 1 --storage local

# 2. Create the app and boot its runner
$ innstance deploy --app my-app

# 3. Save credentials from env/files after the app exists
$ innstance 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
$ innstance apply --app my-app

# 5. Open a tmux-backed interactive session
$ innstance 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
$ innstance machines console input  --app my-app $SESSION "<command>"
$ innstance machines console input  --app my-app $SESSION --key Enter
$ innstance 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:

$ innstance 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

For long-running setup, emit a unique marker after each command and poll the console output until it lands.

$ innstance machines console input --app my-app $SESSION \
    "apt-get install -y X Y Z; echo __DONE__rc=\$?"
$ innstance machines console input --app my-app $SESSION --key Enter

# Poll every ~10s until the marker shows up:
$ innstance --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).

Readiness: Deploy boots the runner. Push code, install dependencies, start the application, and verify its public URL. Plain innstance 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 innstance apply from that project directory. The account plan and available pool limit the requested resources.

$ innstance apply
$ innstance 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 [[volumes]] block 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.

$ innstance machines volumes create mydata --mount /data --size 5 --app my-app
$ innstance 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 Alerts

An alert reports when spending reaches your threshold; it does not stop spending.

$ innstance billing alert set --max 20    # Warn at $20/mo
$ innstance billing alert                 # Check alert + current spend
$ innstance billing alert off             # Remove

JSON Output

# App list
$ innstance status --json | jq '.apps[].name'

# Deploy + capture URL
$ URL=$(innstance deploy --app x --json | jq -r '.url')

# Machine details
$ innstance machines --app x --json | jq '.machines[] | {id, state, region}'

Config File Reference

innstance deploy requires impossible.toml in the current project directory. Run innstance 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

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 innstance deploy --region sin or region = "sin" in impossible.toml.

Multi-Account

$ innstance login                       # Add account 1
$ innstance login                       # Add account 2
$ innstance login --switch              # Pick by number
$ innstance logout                      # Remove active

# CI override (ignores saved profiles)
$ IMPOSSIBLE_API_TOKEN="$IFHOST_TOKEN" innstance 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 innstance 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: innstance 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 innstance machines restart --app x after all changes, or pass --restart on the last command.

CLI out of date

$ innstance version     # Check
$ innstance update      # Update

QR images and deployment status

GET /qr?text=<url>&size=512 accepts HTTPS links on the platform domain and its subdomains. Size must be an integer from 64 to 1024 pixels; invalid sizes return 400. Other domains are not supported.

A runner deployment reports staged when its machine is ready for setup. Install and start your app, then check its URL before treating it as serving traffic. This deployment result does not change when you start a process later.

Domain add prints the required DNS records and exits by default. Add --wait to wait up to two minutes for ownership verification.