---
title: "Deploy an application"
description: "Deploy a VM-based application with one Agent API call: provision compute, run a setup script, open firewall ports, attach SSH keys and add CDN and DNS."
url: "https://edge.network/docs/agent/deploy-app"
section: "Agent tooling"
---

# Deploy an application

Provision a VM, run a setup script, and optionally add CDN and DNS, all in one call.

## Compute sizes

Simplified size names that map to specific resource allocations:

| Size | vCPU | RAM | Disk | Use case |
|---|---|---|---|---|
| `nano` | 1 | 1 GB | 20 GB | Testing, small scripts |
| `small` | 1 | 2 GB | 40 GB | Simple apps, APIs |
| `medium` | 2 | 4 GB | 80 GB | Production apps |
| `large` | 4 | 8 GB | 160 GB | High-traffic apps |
| `xlarge` | 8 | 16 GB | 320 GB | Databases, heavy workloads |

## Request

```http
POST /agent/deploy/app
Authorization: Bearer ea_live_...

{
  "project": "my-saas-app",
  "compute": {
    "os": "ubuntu-24.04",
    "size": "small",
    "region": "london",
    "setup_script": "#!/bin/bash\napt update && apt install -y docker.io\ndocker run -d -p 8080:8080 myapp:latest",
    "ssh_key": "ssh-ed25519 AAAA..."
  },
  "networking": {
    "firewall_rules": [
      { "port": 80, "protocol": "tcp" },
      { "port": 443, "protocol": "tcp" },
      { "port": 22, "protocol": "tcp", "source": "203.0.113.0/24" }
    ]
  },
  "domain": "app.example.com",
  "cdn": {
    "enabled": true
  }
}
```

Instead of an inline `setup_script`, you can reference a tested script from the [startup script library](/docs/compute/scripts) by ID (discover them via `GET /agent/compute/scripts`):

```json
{
  "project": "my-blog",
  "compute": {
    "size": "small",
    "script": "wordpress",
    "script_params": {
      "site_title": "My Blog",
      "admin_email": "me@example.com"
    },
    "ssh_key": "ssh-ed25519 AAAA..."
  }
}
```

## Parameters

| Field | Required | Description |
|---|---|---|
| `project` | Yes | Project name |
| `compute.size` | Yes | `nano`, `small`, `medium`, `large` or `xlarge` |
| `compute.os` | No | Operating system. Default: `ubuntu-24.04` |
| `compute.region` | No | Deployment region. Default: `london` |
| `compute.script` | No | Startup script ID from `GET /agent/compute/scripts` (e.g. `docker`, `nodejs`). Preferred over `setup_script` because library scripts are tested and maintained. |
| `compute.script_params` | No | Parameters for the chosen script, e.g. `{"db_password": "..."}` |
| `compute.setup_script` | No | Inline bash script to run on first boot. Mutually exclusive with `compute.script`. |
| `compute.ssh_key` | No | SSH **public** key string (e.g. `ssh-ed25519 AAAA...`). Auto-creates a key on the account and attaches it. Connect afterwards with `ssh root@<ip>`. |
| `compute.ssh_key_ids` | No | IDs of existing SSH keys (from `/agent/compute/ssh-keys`) to attach. |
| `networking.firewall_rules[]` | No | Inbound firewall rules for the VM |
| `networking.firewall_rules[].port` | Yes (in rule) | Port number (e.g. 80, 443, 8080) |
| `networking.firewall_rules[].protocol` | No | `tcp` (default) or `udp` |
| `networking.firewall_rules[].source` | No | Source CIDR, default `0.0.0.0/0` (anywhere) |
| `domain` | No | Domain for the app |
| `cdn.enabled` | No | Place CDN in front of the VM |

## Firewall rules

Firewall rules in the deploy request create a security group that is automatically attached to the VM. Each rule defines an inbound port, protocol and optional source CIDR.

Agents can also manage security groups independently via the `/agent/compute/firewall` endpoints. They can create groups, attach them to VMs or detach them as needed.

> [!WARNING]
> New VMs have no firewall by default, so all ports are open. If you attach a custom security group, make sure it includes an inbound rule for **tcp/22** or you will lock yourself out of SSH.

## SSH access

Every Edge VM uses `root` as the default SSH user (regardless of OS). Connect with `ssh root@<vm-ip>` using a private key whose public counterpart is attached to the VM.

### At creation time (preferred)

Pass the user's public key as `compute.ssh_key`, or attach pre-saved keys via `compute.ssh_key_ids`. The key is baked in via cloud-init at first boot.

### After creation

If the VM already exists, **do not recreate it**. Save the key on the account, then attach it to the running VM. Edge pushes the key into `authorized_keys` via the guest agent within ~10 seconds.

#### 1. Save the user's public key on the account

```http
POST /agent/compute/ssh-keys
Authorization: Bearer ea_live_...

{
  "name": "alice-laptop",
  "public_key": "ssh-ed25519 AAAA... alice@laptop"
}

# Response:
{
  "ssh_key": { "id": "key_abc123", "name": "alice-laptop", "fingerprint": "SHA256:..." },
  "next_steps": [
    "Pass this key ID in compute.ssh_key_ids when creating a VM, OR",
    "Attach to an existing VM with: POST /agent/compute/vms/{vmId}/ssh-keys/key_abc123"
  ]
}
```

#### 2. Attach the key to the existing VM

There is no need to recreate the VM.

```http
POST /agent/compute/vms/vm_xyz789/ssh-keys/key_abc123
Authorization: Bearer ea_live_...

# Response:
{
  "attached": true,
  "vm_id": "vm_xyz789",
  "ssh_key_id": "key_abc123",
  "ssh": {
    "user": "root",
    "host": "64.34.86.100",
    "command": "ssh root@64.34.86.100",
    "note": "Wait ~10s for guest-agent push, then connect with your matching private key."
  },
  "tell_user": "SSH key attached to VM \"my-saas-app\". Key is being pushed to the running VM via the guest agent (typically <10s). Connect with: ssh root@64.34.86.100"
}
```

### Connection timing

- **VM provisioning:** ~30–60 seconds until `vm.status === "running"`
- **Key propagation on first boot:** an additional ~30–90 seconds
- **Key push to a running VM (guest-agent path):** ~10 seconds
- **Polling:** poll `GET /agent/compute/vms/{id}`; the response includes a ready-to-use `ssh.command` field once the VM is connectable

### If the user has no SSH key yet

Have them generate one locally. The private key never leaves their machine:

```bash
ssh-keygen -t ed25519 -C "edge-network"
cat ~/.ssh/id_ed25519.pub   # paste this into ssh_key
```

### SSH endpoints

| Method | Endpoint | Description |
|---|---|---|
| `GET` | `/agent/compute/ssh-keys` | List SSH keys on the account |
| `POST` | `/agent/compute/ssh-keys` | Save a public key on the account (returns `keyId`) |
| `DELETE` | `/agent/compute/ssh-keys/{id}` | Delete a key from the account (must be detached first) |
| `GET` | `/agent/compute/vms/{id}/ssh-keys` | List keys attached to a specific VM |
| `POST` | `/agent/compute/vms/{id}/ssh-keys/{keyId}` | Attach an existing key to a VM (works on running VMs) |
| `DELETE` | `/agent/compute/vms/{id}/ssh-keys/{keyId}` | Detach a key (removes from `authorized_keys` on running VMs) |

## Available endpoints

All compute endpoints available to agents:

| Method | Endpoint | Description |
|---|---|---|
| `GET` | `/agent/compute/vms` | List VMs |
| `GET` | `/agent/compute/vms/{id}` | VM details with live metrics |
| `GET` | `/agent/compute/os-templates` | Available operating systems |
| `GET` | `/agent/compute/scripts` | Startup scripts (Edge library + custom) with parameters |
| `GET` | `/agent/compute/regions` | Available deployment regions |
| `GET` | `/agent/compute/ssh-keys` | List SSH keys |
| `POST` | `/agent/compute/ssh-keys` | Save an SSH public key |
| `POST` | `/agent/compute/vms/{id}/ssh-keys/{keyId}` | Attach SSH key to existing VM |
| `DELETE` | `/agent/compute/vms/{id}/ssh-keys/{keyId}` | Detach SSH key from VM |
| `GET` | `/agent/compute/firewall` | List security groups |
| `GET` | `/agent/compute/firewall/{id}` | Security group details |
| `POST` | `/agent/compute/firewall` | Create security group |
| `POST` | `/agent/compute/vms/{id}/firewall/{groupId}` | Attach security group |
| `DELETE` | `/agent/compute/vms/{id}/firewall/{groupId}` | Detach security group |

## Response

```json
{
  "status": "provisioning",
  "project_id": "proj_b2c3d4e5f6a7",
  "vm": {
    "id": "vm_xyz789",
    "name": "my-saas-app",
    "status": "provisioning",
    "ip": "64.34.86.100",
    "specs": "1 vCPU, 2 GB RAM, 40 GB disk"
  },
  "ssh": {
    "user": "root",
    "host": "64.34.86.100",
    "command": "ssh root@64.34.86.100",
    "ready": false,
    "note": "Wait until vm.status is \"running\" (~30-60s) and key propagation completes (~30-90s on first boot)."
  },
  "next_steps": [
    "Poll: GET /agent/compute/vms/vm_xyz789 until vm.status === \"running\"",
    "Then connect: ssh root@64.34.86.100"
  ],
  "resources_created": [
    { "type": "compute_vm", "id": "vm_xyz789", "name": "my-saas-app", "size": "small" },
    { "type": "cdn_deployment", "id": "dep_abc123" }
  ],
  "tell_user": "VM \"my-saas-app\" is being provisioned (small: 1 vCPU, 2GB RAM). IP: 64.34.86.100. Your setup script will run on first boot. SSH ready once status is \"running\". Connect: ssh root@64.34.86.100."
}
```

> [!NOTE]
> The VM starts in `provisioning` status. It typically takes 30–60 seconds to become `running`. Use the health check endpoint to monitor.

## Health check

Check the status of all resources in a project:

```http
GET /agent/projects/proj_b2c3d4e5f6a7/health
Authorization: Bearer ea_live_...

# Response:
{
  "project": "my-saas-app",
  "overall_status": "healthy",
  "resources": [
    {
      "type": "compute",
      "id": "vm_xyz789",
      "name": "my-saas-app",
      "status": "running",
      "metrics": {
        "cpu_percent": 42,
        "ram_percent": 61,
        "disk_percent": 28
      }
    },
    {
      "type": "cdn",
      "id": "dep_abc123",
      "name": "my-saas-app-cdn",
      "status": "active",
      "domains": 1
    }
  ],
  "summary": { "total": 2, "healthy": 2, "issues": 0 },
  "tell_user": "Project \"my-saas-app\" is healthy. All 2 resources are running normally."
}
```

## Scaling

Scale compute resources up or down:

```http
POST /agent/projects/proj_b2c3d4e5f6a7/scale
Authorization: Bearer ea_live_...

{
  "compute": { "size": "medium" },
  "reason": "Traffic increased 3x this week, CPU consistently above 80%"
}

# Response:
{
  "project": "my-saas-app",
  "scaled": [
    { "resource": "vm_xyz789", "name": "my-saas-app", "scaled_to": "medium",
      "specs": { "vcpu": 2, "ram": 4, "disk": 80 } }
  ],
  "reason": "Traffic increased 3x this week, CPU consistently above 80%",
  "tell_user": "Scaled 1 resource in \"my-saas-app\" to medium."
}
```

## See also

- [Deploy a static site](/docs/agent/deploy-static-site): Deploy a static website in one call
- [Projects](/docs/agent/projects): Health checks, scaling and teardown
