$npx -y skills add digitalocean-labs/do-app-platform-skills --skill spacesConfigure DigitalOcean Spaces (S3-compatible object storage) for App Platform apps. Use when setting up file uploads, static assets, CDN, access logging, or per-app credential management.
| 1 | # Spaces Skill |
| 2 | |
| 3 | S3-compatible object storage for App Platform applications. |
| 4 | |
| 5 | ## Tool Separation (Critical) |
| 6 | |
| 7 | ``` |
| 8 | ┌─────────────────────────────────────────────────────────────────┐ |
| 9 | │ doctl: KEYS ONLY │ aws CLI: EVERYTHING ELSE │ |
| 10 | │ • doctl spaces keys create│ • Bucket create/delete │ |
| 11 | │ • doctl spaces keys list │ • Object upload/download │ |
| 12 | │ • doctl spaces keys delete│ • CORS, logging, lifecycle │ |
| 13 | └─────────────────────────────────────────────────────────────────┘ |
| 14 | ``` |
| 15 | |
| 16 | > **Why?** doctl's Spaces support is limited to key management. Bucket operations require S3-compatible tools. |
| 17 | |
| 18 | --- |
| 19 | |
| 20 | ## Quick Decision |
| 21 | |
| 22 | ``` |
| 23 | ┌─────────────────────────────────────────────────────────────────┐ |
| 24 | │ What do you need to do with Spaces? │ |
| 25 | └─────────────────────────────────────────────────────────────────┘ |
| 26 | │ |
| 27 | ┌─────────────────────┼─────────────────────┐ |
| 28 | │ │ │ |
| 29 | Create key Create bucket Upload/download |
| 30 | or rotate set CORS/logging objects |
| 31 | │ │ │ |
| 32 | ▼ ▼ ▼ |
| 33 | ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ |
| 34 | │ doctl │ │ aws CLI │ │ aws CLI │ |
| 35 | │ spaces keys │ │ s3api │ │ s3 cp/sync │ |
| 36 | └───────────────┘ └───────────────┘ └───────────────┘ |
| 37 | ``` |
| 38 | |
| 39 | --- |
| 40 | |
| 41 | ## Prerequisites |
| 42 | |
| 43 | ```bash |
| 44 | doctl auth init # One-time DO API auth |
| 45 | aws --version # AWS CLI v2 |
| 46 | jq --version # JSON processor |
| 47 | ``` |
| 48 | |
| 49 | --- |
| 50 | |
| 51 | ## Quick Start |
| 52 | |
| 53 | ### 1. Set Environment |
| 54 | |
| 55 | ```bash |
| 56 | # Choose region matching your App Platform app (see shared/regions.yaml) |
| 57 | export DO_SPACES_REGION="syd1" |
| 58 | export DO_SPACES_ENDPOINT="https://${DO_SPACES_REGION}.digitaloceanspaces.com" |
| 59 | export APP_NAME="myapp" |
| 60 | export BUCKET="${APP_NAME}-uploads" |
| 61 | export DO_SPACES_KEY_NAME="${APP_NAME}-spaces-key" |
| 62 | ``` |
| 63 | |
| 64 | ### 2. Create Key (doctl) |
| 65 | |
| 66 | ```bash |
| 67 | KEY_JSON=$(doctl spaces keys create "${DO_SPACES_KEY_NAME}" --output json) |
| 68 | export AWS_ACCESS_KEY_ID=$(echo "$KEY_JSON" | jq -r '.[0].access_key') |
| 69 | export AWS_SECRET_ACCESS_KEY=$(echo "$KEY_JSON" | jq -r '.[0].secret_key') |
| 70 | |
| 71 | # IMPORTANT: Secret shown only once - save it now! |
| 72 | ``` |
| 73 | |
| 74 | ### 3. Create Bucket (aws CLI) |
| 75 | |
| 76 | ```bash |
| 77 | aws --endpoint-url "$DO_SPACES_ENDPOINT" s3api create-bucket --bucket "$BUCKET" |
| 78 | ``` |
| 79 | |
| 80 | ### 4. App Spec |
| 81 | |
| 82 | ```yaml |
| 83 | services: |
| 84 | - name: api |
| 85 | envs: |
| 86 | - key: SPACES_BUCKET |
| 87 | value: myapp-uploads |
| 88 | - key: SPACES_REGION |
| 89 | value: ${SPACES_REGION} # Your bucket's region |
| 90 | - key: SPACES_ENDPOINT |
| 91 | value: ${SPACES_ENDPOINT} # e.g., https://syd1.digitaloceanspaces.com |
| 92 | - key: SPACES_ACCESS_KEY |
| 93 | scope: RUN_TIME |
| 94 | type: SECRET |
| 95 | value: ${SPACES_ACCESS_KEY} |
| 96 | - key: SPACES_SECRET_KEY |
| 97 | scope: RUN_TIME |
| 98 | type: SECRET |
| 99 | value: ${SPACES_SECRET_KEY} |
| 100 | ``` |
| 101 | |
| 102 | > Store `SPACES_ACCESS_KEY` and `SPACES_SECRET_KEY` in GitHub Secrets. |
| 103 | |
| 104 | --- |
| 105 | |
| 106 | ## Regions |
| 107 | |
| 108 | Spaces uses different slugs than App Platform. See [shared/regions.yaml](../../shared/regions.yaml). |
| 109 | |
| 110 | | App Platform | Spaces | Endpoint | |
| 111 | |--------------|--------|----------| |
| 112 | | `nyc` | `nyc3` | `https://nyc3.digitaloceanspaces.com` | |
| 113 | | `sfo` | `sfo3` | `https://sfo3.digitaloceanspaces.com` | |
| 114 | | `ams` | `ams3` | `https://ams3.digitaloceanspaces.com` | |
| 115 | | `lon` | `lon1` | `https://lon1.digitaloceanspaces.com` | |
| 116 | | `fra` | `fra1` | `https://fra1.digitaloceanspaces.com` | |
| 117 | | `tor` | `tor1` | `https://tor1.digitaloceanspaces.com` | |
| 118 | | `sgp` | `sgp1` | `https://sgp1.digitaloceanspaces.com` | |
| 119 | | `blr` | `blr1` | `https://blr1.digitaloceanspaces.com` | |
| 120 | | `syd` | `syd1` | `https://syd1.digitaloceanspaces.com` | |
| 121 | | `atl` | `atl1` | `https://atl1.digitaloceanspaces.com` | |
| 122 | |
| 123 | --- |
| 124 | |
| 125 | ## Common Operations |
| 126 | |
| 127 | ### doctl (Keys Only) |
| 128 | |
| 129 | ```bash |
| 130 | doctl spaces keys list |
| 131 | doctl spaces keys create "myapp-key" --output json |
| 132 | doctl spaces keys delete <key-id> |
| 133 | ``` |
| 134 | |
| 135 | ### aws CLI (Buckets & Objects) |
| 136 | |
| 137 | ```bash |
| 138 | EP="--endpoint-url https://syd1.digitaloceanspaces.com" |
| 139 | |
| 140 | # Buckets |
| 141 | aws $EP s3 ls |
| 142 | aws $EP s3api create-bucket --bucket myapp-uploads |
| 143 | aws $EP s3 rb s3://myapp-uploads |
| 144 | |
| 145 | # Objects |
| 146 | aws $EP s3 cp ./file.txt s3://myapp-uploads/path/file.txt |
| 147 | aws $EP s3 cp s3://myapp-uploads/path/file.txt ./file.txt |
| 148 | aws $EP s3 ls s3://myapp-uploads/ --recursive |
| 149 | aws $EP s3 sync ./local-dir/ s3://myapp-uploads/prefix/ |
| 150 | ``` |
| 151 | |
| 152 | --- |
| 153 | |
| 154 | ## Scripts (AI-Friendly) |
| 155 | |
| 156 | | Script | Purpose | |
| 157 | |--------|---------| |
| 158 | | `scripts/bootstrap_app_spaces.sh` | Full setup: key + buckets + logging | |
| 159 | | `scripts/enable_bucket_logging.sh` | Enable/verify logging (idempotent) | |
| 160 | | `scripts/ |