spaces
Spaces Skill
S3-compatible object storage for App Platform applications.
Tool Separation (Critical)
┌─────────────────────────────────────────────────────────────────┐
│ doctl: KEYS ONLY │ aws CLI: EVERYTHING ELSE │
│ • doctl spaces keys create│ • Bucket create/delete │
│ • doctl spaces keys list │ • Object upload/download │
│ • doctl spaces keys delete│ • CORS, logging, lifecycle │
└─────────────────────────────────────────────────────────────────┘
Why? doctl's Spaces support is limited to key management. Bucket operations require S3-compatible tools.
Quick Decision
┌─────────────────────────────────────────────────────────────────┐
│ What do you need to do with Spaces? │
└─────────────────────────────────────────────────────────────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
Create key Create bucket Upload/download
or rotate set CORS/logging objects
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ doctl │ │ aws CLI │ │ aws CLI │
│ spaces keys │ │ s3api │ │ s3 cp/sync │
└───────────────┘ └───────────────┘ └───────────────┘
Prerequisites
doctl auth init # One-time DO API auth
aws --version # AWS CLI v2
jq --version # JSON processor
Quick Start
1. Set Environment
# Choose region matching your App Platform app (see shared/regions.yaml)
export DO_SPACES_REGION="syd1"
export DO_SPACES_ENDPOINT="https://${DO_SPACES_REGION}.digitaloceanspaces.com"
export APP_NAME="myapp"
export BUCKET="${APP_NAME}-uploads"
export DO_SPACES_KEY_NAME="${APP_NAME}-spaces-key"
2. Create Key (doctl)
KEY_JSON=$(doctl spaces keys create "${DO_SPACES_KEY_NAME}" --output json)
export AWS_ACCESS_KEY_ID=$(echo "$KEY_JSON" | jq -r '.[0].access_key')
export AWS_SECRET_ACCESS_KEY=$(echo "$KEY_JSON" | jq -r '.[0].secret_key')
# IMPORTANT: Secret shown only once - save it now!
3. Create Bucket (aws CLI)
aws --endpoint-url "$DO_SPACES_ENDPOINT" s3api create-bucket --bucket "$BUCKET"
4. App Spec
services:
- name: api
envs:
- key: SPACES_BUCKET
value: myapp-uploads
- key: SPACES_REGION
value: ${SPACES_REGION} # Your bucket's region
- key: SPACES_ENDPOINT
value: ${SPACES_ENDPOINT} # e.g., https://syd1.digitaloceanspaces.com
- key: SPACES_ACCESS_KEY
scope: RUN_TIME
type: SECRET
value: ${SPACES_ACCESS_KEY}
- key: SPACES_SECRET_KEY
scope: RUN_TIME
type: SECRET
value: ${SPACES_SECRET_KEY}
Store
SPACES_ACCESS_KEYandSPACES_SECRET_KEYin GitHub Secrets.
Regions
Spaces uses different slugs than App Platform. See shared/regions.yaml.
| App Platform | Spaces | Endpoint |
|---|---|---|
nyc |
nyc3 |
https://nyc3.digitaloceanspaces.com |
sfo |
sfo3 |
https://sfo3.digitaloceanspaces.com |
ams |
ams3 |
https://ams3.digitaloceanspaces.com |
lon |
lon1 |
https://lon1.digitaloceanspaces.com |
fra |
fra1 |
https://fra1.digitaloceanspaces.com |
tor |
tor1 |
https://tor1.digitaloceanspaces.com |
sgp |
sgp1 |
https://sgp1.digitaloceanspaces.com |
blr |
blr1 |
https://blr1.digitaloceanspaces.com |
syd |
syd1 |
https://syd1.digitaloceanspaces.com |
atl |
atl1 |
https://atl1.digitaloceanspaces.com |
Common Operations
doctl (Keys Only)
doctl spaces keys list
doctl spaces keys create "myapp-key" --output json
doctl spaces keys delete <key-id>
aws CLI (Buckets & Objects)
EP="--endpoint-url https://syd1.digitaloceanspaces.com"
# Buckets
aws $EP s3 ls
aws $EP s3api create-bucket --bucket myapp-uploads
aws $EP s3 rb s3://myapp-uploads
# Objects
aws $EP s3 cp ./file.txt s3://myapp-uploads/path/file.txt
aws $EP s3 cp s3://myapp-uploads/path/file.txt ./file.txt
aws $EP s3 ls s3://myapp-uploads/ --recursive
aws $EP s3 sync ./local-dir/ s3://myapp-uploads/prefix/
Scripts (AI-Friendly)
| Script | Purpose |
|---|---|
scripts/bootstrap_app_spaces.sh |
Full setup: key + buckets + logging |
scripts/enable_bucket_logging.sh |
Enable/verify logging (idempotent) |
scripts/view_access_logs.sh |
List/download access logs |
scripts/rotate_spaces_key.sh |
Rotate credentials safely |
# Set env vars then run
./scripts/bootstrap_app_spaces.sh
Reference Files
| File | Content |
|---|---|
| aws-cli-operations.md | Complete aws CLI reference |
| key-management.md | Per-app keys, rotation workflow |
| access-logging.md | Bucket logging setup |
| sdk-configuration.md | Node.js, Python, Go SDK setup |
| troubleshooting.md | Common errors and fixes |
URL Patterns
| Type | Format |
|---|---|
| Standard | https://<bucket>.<region>.digitaloceanspaces.com/<key> |
| CDN | https://<bucket>.<region>.cdn.digitaloceanspaces.com/<key> |
Quick Troubleshooting
| Error | Fix |
|---|---|
| BucketAlreadyExists (409) | Use unique prefix: mycompany-myapp-uploads |
| Access Denied (403) | Verify keys, check endpoint matches bucket region |
| CORS error | Configure via aws s3api put-bucket-cors |
| SignatureDoesNotMatch | Use https:// prefix, no trailing slash |
See troubleshooting.md for details.
Integration
- → designer: Includes Spaces env vars when architecting apps
- → deployment: Credentials stored in GitHub Secrets
- → devcontainers: MinIO provides local Spaces parity
More from digitalocean-labs/do-app-platform-skills
app-platform-router
Routes DigitalOcean App Platform tasks to specialized sub-skills. Use when working with App Platform deployments, migrations, database configuration, networking, or troubleshooting.
71devcontainers
Set up local development environments with production parity for DigitalOcean App Platform. Use when setting up local dev, adding devcontainer to a project, running App Platform apps locally, or configuring backing services (Postgres, Redis, Kafka, S3).
1app-platform-networking
Configure domains, routing, CORS, VPC, static IPs, and inter-service communication for DigitalOcean App Platform. Use when setting up custom domains, subdomain routing, cross-origin API access, or secure database connectivity.
1app-platform-troubleshooting
Debug running App Platform applications by accessing containers, analyzing logs, running diagnostics, and applying fixes. Use when apps fail to deploy, crash at runtime, have connectivity issues, or need performance diagnosis.
1