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

Deployment Targets

TargetBest ForCommand
Fly.ioQuick cloud deployment, global edge--target fly
KubernetesEnterprise, existing K8s clusters--target kubernetes
AnsibleBare 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

# 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:
{
  "mcpServers": {
    "my-station": {
      "url": "https://my-station.fly.dev/mcp"
    }
  }
}

Fly.io Options

# 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

# 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

# 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:
# 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 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

# 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

# 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:
[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

cd /tmp/ansible-deploy
ansible-playbook -i inventory.ini playbook.yml

Verify Deployment

After deployment, verify Station is running:
# 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.
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.

Supported Backends

BackendURI FormatExample
AWS Secrets Manageraws-secretsmanager://aws-secretsmanager://station/prod
AWS SSM Parameter Storeaws-ssm://aws-ssm:///station/prod/
HashiCorp Vaultvault://vault://secret/data/station/prod
GCP Secret Managergcp-secretmanager://gcp-secretmanager://projects/my-project/secrets/station
SOPSsops://sops://./secrets/prod.enc.yaml

AWS SSM Parameter Store

Store secrets as SSM parameters:
# 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:
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:
aws secretsmanager create-secret \
  --name station/prod \
  --secret-string '{"OPENAI_API_KEY":"sk-...","STN_CLOUDSHIP_KEY":"csk_..."}'
Deploy:
stn deploy my-env --target k8s \
  --secrets-backend aws-secretsmanager \
  --secrets-path station/prod

HashiCorp Vault

# 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:
export VAULT_ADDR=https://vault.example.com
export VAULT_TOKEN=hvs.xxx

Supported Secret Keys

Station automatically maps these secret keys to configuration:
Secret KeyConfig Field
STN_AI_API_KEYAI provider API key
OPENAI_API_KEYOpenAI API key (fallback)
ANTHROPIC_API_KEYAnthropic API key (fallback)
STN_CLOUDSHIP_KEYCloudShip registration key
STN_CLOUDSHIP_ENDPOINTCloudShip endpoint
STN_CLOUDSHIP_NAMEStation name in CloudShip
All other secrets are injected as environment variables.

Export Variables

See what secrets your deployment needs:
# 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:
# 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:
stn deploy my-env --target fly  # CloudShip config from local station is included
Or set manually:
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:
stn deploy my-env --target fly  # Rebuilds and redeploys

Destroy Deployment

# 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

# 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

# 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

# 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

# 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:
# 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):
# 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