> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cloudshipai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Cloud Deployment

> Deploy Station to Fly.io, Kubernetes, and bare metal servers

The `stn deploy` command provides production-ready deployment to multiple platforms with built-in secrets management, persistent storage, and CloudShip integration.

## Deployment Targets

| Target         | Best For                            | Command               |
| -------------- | ----------------------------------- | --------------------- |
| **Fly.io**     | Quick cloud deployment, global edge | `--target fly`        |
| **Kubernetes** | Enterprise, existing K8s clusters   | `--target kubernetes` |
| **Ansible**    | Bare metal, VMs, on-premise         | `--target ansible`    |

## Fly.io Deployment

The fastest path to production. Deploys Station with persistent storage, automatic TLS, and global edge distribution.

### Quick Deploy

```bash theme={null}
# Deploy an environment to Fly.io
stn deploy my-env --target fly

# Deploy with a custom app name
stn deploy my-env --target fly --name my-station

# Deploy to a specific region
stn deploy my-env --target fly --region syd
```

### What Gets Deployed

* Docker image with your agents baked in
* 3GB persistent volume for database and configs
* Dedicated IPv4 address
* Automatic TLS certificate
* All secrets set via `fly secrets`

### Example Output

```
🚀 Deploying environment 'my-env' to fly (region: ord)

✓ Built image: station-my-env:latest
✓ App created: my-station
✓ 14 secrets set
✓ IPv4 allocated
✓ Volume created
✓ Deployed to Fly.io

✅ Deployment Complete!

🤖 Agent MCP Endpoint:
   https://my-station.fly.dev/mcp

📋 Available Agents (3):
   - agent_cost-analyzer
   - agent_security-scanner
   - agent_deployment-helper
```

### Add to Claude Desktop

After deployment, add the MCP endpoint to Claude Desktop:

```json theme={null}
{
  "mcpServers": {
    "my-station": {
      "url": "https://my-station.fly.dev/mcp"
    }
  }
}
```

### Fly.io Options

```bash theme={null}
# Enable auto-stop when idle (saves cost, but loses persistent CloudShip connection)
stn deploy my-env --target fly --auto-stop

# Tear down deployment
stn deploy my-env --target fly --destroy

# Enable development UI
fly secrets set STN_DEV_MODE=true --app my-station
```

## Kubernetes Deployment

Generates production-ready Kubernetes manifests or deploys directly to your cluster.

### Generate Manifests

```bash theme={null}
# Generate manifests to a directory
stn deploy my-env --target kubernetes --dry-run --output-dir ./k8s/

# Preview what would be generated
stn deploy my-env --target k8s --dry-run
```

### Deploy to Cluster

```bash theme={null}
# Deploy to default namespace
stn deploy my-env --target kubernetes

# Deploy to specific namespace
stn deploy my-env --target k8s --namespace production

# Use specific kubectl context
stn deploy my-env --target k8s --context my-cluster
```

### Generated Resources

* **Deployment** - Station container with agents
* **Service** - ClusterIP for internal access
* **Secret** - API keys and CloudShip credentials
* **PersistentVolumeClaim** - Database and config storage
* **ConfigMap** - Environment configuration

### Runtime Secrets

For production, use runtime secrets instead of baking them into manifests:

```bash theme={null}
# Station fetches secrets from AWS SSM at startup
stn deploy my-env --target k8s \
  --secrets-backend aws-ssm \
  --secrets-path /station/prod/
```

See [Secrets Management](#secrets-management) below.

## Ansible Deployment

Deploys Station to any Linux server via SSH. Generates an Ansible playbook that:

* Installs Docker
* Pulls the Station image
* Configures systemd service
* Sets up persistent storage

### Generate Playbook

```bash theme={null}
# Preview the generated files
stn deploy my-env --target ansible --dry-run --output-dir /tmp/ansible-deploy

# Generated files:
# - playbook.yml
# - inventory.ini
# - vars/main.yml
# - templates/docker-compose.yml.j2
# - templates/station.service.j2
```

### Deploy to Servers

```bash theme={null}
# Deploy to servers specified in inventory
stn deploy my-env --target ansible

# Specify hosts directly
stn deploy my-env --target ansible --hosts user@server1.example.com --hosts user@server2.example.com

# Use custom SSH key
stn deploy my-env --target ansible --ssh-key ~/.ssh/deploy_key --ssh-user deploy
```

### Inventory Configuration

The generated `inventory.ini`:

```ini theme={null}
[station_servers]
192.168.1.100 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa

[station_servers:vars]
ansible_python_interpreter=/usr/bin/python3
```

### Manual Playbook Run

```bash theme={null}
cd /tmp/ansible-deploy
ansible-playbook -i inventory.ini playbook.yml
```

### Verify Deployment

After deployment, verify Station is running:

```bash theme={null}
# Check service status
ssh user@server systemctl status station-my-env

# Check logs
ssh user@server docker logs station-my-env

# Test agent endpoint
curl http://server:8587/health
```

## Secrets Management

Station supports runtime secrets fetching from external secret stores. Instead of baking secrets into deployment configs, the container fetches them at startup.

<Note>
  Runtime secrets loading requires Station v0.24.9+ which includes AWS CLI in the container image. The container fetches secrets at startup before the Station process begins serving requests.
</Note>

### Supported Backends

| Backend                 | URI Format              | Example                                                   |
| ----------------------- | ----------------------- | --------------------------------------------------------- |
| AWS Secrets Manager     | `aws-secretsmanager://` | `aws-secretsmanager://station/prod`                       |
| AWS SSM Parameter Store | `aws-ssm://`            | `aws-ssm:///station/prod/`                                |
| HashiCorp Vault         | `vault://`              | `vault://secret/data/station/prod`                        |
| GCP Secret Manager      | `gcp-secretmanager://`  | `gcp-secretmanager://projects/my-project/secrets/station` |
| SOPS                    | `sops://`               | `sops://./secrets/prod.enc.yaml`                          |

### AWS SSM Parameter Store

Store secrets as SSM parameters:

```bash theme={null}
# Create parameters
aws ssm put-parameter --name "/station/prod/OPENAI_API_KEY" --value "sk-..." --type SecureString
aws ssm put-parameter --name "/station/prod/STN_CLOUDSHIP_KEY" --value "csk_..." --type SecureString
```

Deploy with SSM backend:

```bash theme={null}
stn deploy my-env --target fly \
  --secrets-backend aws-ssm \
  --secrets-path /station/prod/
```

At startup, Station loads all parameters under `/station/prod/` and injects them into config:

```
🔐 Loading secrets from backend: aws-ssm (path: /station/prod/)
   Injected: [OPENAI_API_KEY→config, STN_CLOUDSHIP_KEY→config]
✅ Loaded 5 secrets from aws-ssm
```

### AWS Secrets Manager

Store secrets as a JSON object:

```bash theme={null}
aws secretsmanager create-secret \
  --name station/prod \
  --secret-string '{"OPENAI_API_KEY":"sk-...","STN_CLOUDSHIP_KEY":"csk_..."}'
```

Deploy:

```bash theme={null}
stn deploy my-env --target k8s \
  --secrets-backend aws-secretsmanager \
  --secrets-path station/prod
```

### HashiCorp Vault

```bash theme={null}
# Store secrets
vault kv put secret/station/prod OPENAI_API_KEY=sk-... STN_CLOUDSHIP_KEY=csk_...

# Deploy with Vault
stn deploy my-env --target k8s \
  --secrets-backend vault \
  --secrets-path secret/data/station/prod
```

Set Vault address and token via environment:

```bash theme={null}
export VAULT_ADDR=https://vault.example.com
export VAULT_TOKEN=hvs.xxx
```

### Supported Secret Keys

Station automatically maps these secret keys to configuration:

| Secret Key               | Config Field                 |
| ------------------------ | ---------------------------- |
| `STN_AI_API_KEY`         | AI provider API key          |
| `OPENAI_API_KEY`         | OpenAI API key (fallback)    |
| `ANTHROPIC_API_KEY`      | Anthropic API key (fallback) |
| `STN_CLOUDSHIP_KEY`      | CloudShip registration key   |
| `STN_CLOUDSHIP_ENDPOINT` | CloudShip endpoint           |
| `STN_CLOUDSHIP_NAME`     | Station name in CloudShip    |

All other secrets are injected as environment variables.

## Export Variables

See what secrets your deployment needs:

```bash theme={null}
# Show required variables
stn deploy export-vars my-env

# Generate .env template
stn deploy export-vars my-env --format env > secrets.env

# For bundle-based deployment
stn deploy export-vars --bundle-id e26b414a-f076-4135-927f-810bc1dc892a
```

## Bundle-Based Deployment

Deploy directly from CloudShip bundles without a local environment:

```bash theme={null}
# Deploy a bundle from CloudShip
stn deploy --bundle-id e26b414a-f076-4135-927f-810bc1dc892a --target fly

# Deploy a local bundle file
stn deploy --bundle ./my-agents.tar.gz --target k8s --name my-station

# Deploy from URL
stn deploy --bundle https://example.com/bundle.tar.gz --target ansible
```

## CloudShip Integration

When `cloudship.enabled: true` in your config, deployed Stations automatically:

* Connect to CloudShip Lighthouse via gRPC
* Report online status with heartbeats
* Accept remote agent execution requests
* Stream telemetry and run data

Configure in deployment:

```bash theme={null}
stn deploy my-env --target fly  # CloudShip config from local station is included
```

Or set manually:

```bash theme={null}
fly secrets set STN_CLOUDSHIP_ENABLED=true --app my-station
fly secrets set STN_CLOUDSHIP_KEY=csk_xxx --app my-station
fly secrets set STN_CLOUDSHIP_ENDPOINT=lighthouse.cloudshipai.com:443 --app my-station
```

## Deployment Lifecycle

### Update Deployment

Re-run deploy to update with new agents or configuration:

```bash theme={null}
stn deploy my-env --target fly  # Rebuilds and redeploys
```

### Destroy Deployment

```bash theme={null}
# Fly.io
stn deploy my-env --target fly --destroy

# Kubernetes
kubectl delete -f ./k8s/

# Ansible - run with station_state=absent
ansible-playbook -i inventory.ini playbook.yml -e "station_state=absent"
```

### Check Status

```bash theme={null}
# Fly.io
fly status --app my-station
fly logs --app my-station

# Kubernetes
kubectl get pods -l app=station
kubectl logs -l app=station

# Ansible-deployed
ssh user@server systemctl status station-my-env
```

## Troubleshooting

### Fly.io Issues

```bash theme={null}
# Check logs
fly logs --app my-station

# SSH into container
fly ssh console --app my-station

# Check secrets
fly secrets list --app my-station
```

### Kubernetes Issues

```bash theme={null}
# Check pod status
kubectl describe pod -l app=station

# Check events
kubectl get events --sort-by='.lastTimestamp'

# View logs
kubectl logs -l app=station --tail=100
```

### Ansible Issues

```bash theme={null}
# Run with verbose output
ansible-playbook -i inventory.ini playbook.yml -vvv

# Check systemd status on remote
ssh user@server journalctl -u station-my-env -f
```

### Secrets Not Loading

Verify the secrets backend is configured:

```bash theme={null}
# Check environment variables in container
fly ssh console --app my-station -C "env | grep STN_SECRETS"

# Should show:
# STN_SECRETS_BACKEND=aws-ssm
# STN_SECRETS_PATH=/station/prod/
```

Verify AWS credentials (for SSM/Secrets Manager):

```bash theme={null}
# Container needs AWS credentials via IAM role or environment
fly secrets set AWS_ACCESS_KEY_ID=xxx --app my-station
fly secrets set AWS_SECRET_ACCESS_KEY=xxx --app my-station
```
