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