DigitalOcean Apps skill for Codex
App specs in git, databases behind trusted sources. Codex exports the live App Platform spec before editing, then manages services, workers, static sites and pre-deploy migration jobs in .do/app.yaml with bindable database variables and encrypted secrets. For Droplets it uses cloud-init, tag-based Cloud Firewalls and reserved IPs, locks managed databases to trusted sources and connection pools, and confirms before deleting anything.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- digitalocean-apps Installs to ~/.agents/skills/digitalocean-apps/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install digitalocean-apps The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project digitalocean-apps Installs to .agents/skills/digitalocean-apps/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/digitalocean-apps.zipunzip -o digitalocean-apps.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) digitalocean-apps PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $digitalocean-apps, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Deploys and manages DigitalOcean workloads with doctl - App Platform app specs (.do/app.yaml) for services, workers, static sites, jobs and managed databases with bindable variables, plus Droplets with cloud-init, Cloud Firewalls, VPCs, reserved IPs, volumes, snapshots, backups, Spaces object storage and managed database trusted sources. Use when deploying to DigitalOcean, writing or fixing an app spec, an App Platform build or health check fails, choosing between App Platform and a Droplet, or locking down a managed database.
Use it when
- Deploying an app to DigitalOcean App Platform or a Droplet
- Writing, exporting or fixing a .do/app.yaml spec
- Locking down a DigitalOcean managed database or Droplet network access
- Choosing between App Platform, Droplets and DOKS
Use something else when
- You are running Docker Compose on any VPS regardless of provider: use vps-deploys
- The workload is on DigitalOcean Kubernetes and needs manifests: use kubernetes-manifests
How to check the result
doctl apps spec validate passes, the committed spec matches doctl apps spec get, health checks return 200, and database trusted sources list only the app or VPC.
The mistake it prevents
Applying an old spec from git after someone changed settings in the console, which silently reverts their changes.
Good DigitalOcean setups keep the App Platform spec in git and apply it with doctl,
attach managed databases through bindable variables instead of pasted URLs, restrict
databases to trusted sources, and treat Droplets as cattle built from cloud-init with a
Cloud Firewall attached before they ever serve traffic.
Inspect first
- Read
AGENTS.md,.do/app.yaml(or.do/deploy.template.yaml),Dockerfile,Procfile, and any Terraform using thedigitaloceanprovider. doctl auth listanddoctl account getshow which team the CLI targets. Everydoctlcall needs network and an API token; in an offline sandbox, write specs and hand commands to the user.- If an app already exists, export the live spec before editing so you do not drop settings made in the UI:
doctl apps list
doctl apps spec get <app-id> > .do/app.yaml
App Platform or Droplet?
| Need | Choose |
|---|---|
| Web app or API from a repo or image, no server management | App Platform |
| Background workers, cron-like jobs, static frontends in one unit | App Platform (workers, jobs, static_sites) |
| Custom daemons, GPU, kernel tuning, unusual ports, SSH access | Droplet |
| Lowest fixed cost for a hobby project with several containers | Droplet with Compose (see vps-deploys) |
| Kubernetes workloads | DOKS (managed Kubernetes) |
App spec
# .do/app.yaml
name: acme
region: fra
services:
- name: api
github: { repo: acme/api, branch: main, deploy_on_push: true }
source_dir: /
build_command: npm ci && npm run build
run_command: node dist/server.js
http_port: 8080
instance_count: 2
instance_size_slug: <size from: doctl apps tier instance-size list>
health_check:
http_path: /healthz
initial_delay_seconds: 10
routes: [{ path: /api }]
envs:
- key: DATABASE_URL
scope: RUN_TIME
value: ${db.DATABASE_URL}
- key: STRIPE_SECRET_KEY
scope: RUN_TIME
type: SECRET
value: placeholder-set-in-console # replaced by encrypted value; never commit the real key
- key: PUBLIC_BASE_URL
scope: RUN_AND_BUILD_TIME
value: ${APP_URL}
workers:
- name: jobs
github: { repo: acme/api, branch: main, deploy_on_push: true }
run_command: node dist/worker.js
envs:
- key: DATABASE_URL
scope: RUN_TIME
value: ${db.DATABASE_URL}
jobs:
- name: migrate
kind: PRE_DEPLOY
github: { repo: acme/api, branch: main }
run_command: npm run migrate
envs:
- key: DATABASE_URL
scope: RUN_TIME
value: ${db.DATABASE_URL}
static_sites:
- name: web
github: { repo: acme/web, branch: main, deploy_on_push: true }
build_command: npm ci && npm run build
output_dir: dist
catchall_document: index.html
routes: [{ path: / }]
databases:
- name: db
engine: PG
production: true
cluster_name: acme-pg # existing managed cluster; omit for a dev database
Spec rules:
scopematters:BUILD_TIMEvalues are not available at runtime and vice versa. Front end public URLs usually needRUN_AND_BUILD_TIME.- Bindable variables (
${db.DATABASE_URL},${db.CA_CERT},${APP_URL},${_self.PRIVATE_URL}) keep secrets out of the spec and update when resources change. - Secrets: after the first apply,
doctl apps spec getreturns encrypted values (EV[...]). Commit those, not plaintext. PRE_DEPLOYjobs run before the new version receives traffic; a failure stops the deploy. Keep migrations backward compatible with the running version.- Dev databases (no
production: true) are for testing only; use a managed cluster for anything real.
Apply and observe
doctl apps spec validate .do/app.yaml
doctl apps create --spec .do/app.yaml # first time
doctl apps update <app-id> --spec .do/app.yaml # later changes; confirm for production
doctl apps create-deployment <app-id> # redeploy without spec change
doctl apps list-deployments <app-id>
doctl apps logs <app-id> api --type build
doctl apps logs <app-id> api --type run --follow
Rollback: redeploy a previous deployment from the console’s deployment history, or point the spec at a known-good image tag or commit. Database changes do not roll back.
Droplets
doctl compute droplet create web-1 \
--image ubuntu-24-04-x64 --size s-2vcpu-4gb --region fra1 \
--vpc-uuid <vpc-id> --ssh-keys <key-fingerprint> \
--enable-monitoring --enable-backups \
--user-data-file cloud-init.yaml --tag-names web,prod --wait
doctl compute firewall create --name web-fw --tag-names web \
--inbound-rules "protocol:tcp,ports:22,address:203.0.113.10/32 protocol:tcp,ports:80,address:0.0.0.0/0,address:::/0 protocol:tcp,ports:443,address:0.0.0.0/0,address:::/0" \
--outbound-rules "protocol:tcp,ports:all,address:0.0.0.0/0,address:::/0 protocol:udp,ports:all,address:0.0.0.0/0,address:::/0"
- Attach firewalls by tag so new Droplets are covered automatically.
- Use a reserved IP for anything DNS points at; reassign it to a replacement Droplet instead of changing DNS.
- Volumes (block storage) hold data that must outlive the Droplet; snapshots before risky changes; weekly or daily backups as a safety net, plus your own off-provider backups.
- Destroying a Droplet or volume is permanent: confirm with the user and check for a recent snapshot first.
Managed databases
doctl databases list
doctl databases firewalls append <db-id> --rule app:<app-id>
doctl databases firewalls append <db-id> --rule tag:web
doctl databases pool create <db-id> api-pool --db app --user doadmin --size 20 --mode transaction
- Restrict trusted sources to the app, the Droplet tag or the VPC. An empty trusted sources list means any IP with credentials can connect.
- Use the connection pool (PgBouncer) URL from apps with many instances; transaction mode breaks session features such as prepared statements in some drivers.
- Connect with
sslmode=requireand pin the CA (${db.CA_CERT}) when the driver supports it.
Spaces
Spaces is S3-compatible: use any S3 SDK with endpoint https://<region>.digitaloceanspaces.com
and Spaces access keys. Enable the CDN per Space for public assets; keep private objects
private and serve them with presigned URLs.
Verification
-
doctl apps spec validatepasses; the spec in git matchesdoctl apps spec get. - Build and run logs are clean; the health check path returns 200.
- Database trusted sources list only the app, tags or VPC.
- Droplets have a firewall attached (
doctl compute firewall list) and SSH limited. - Production spec updates, Droplet or database deletions happen only after explicit user confirmation.
Gotchas
- Editing settings in the console and then applying an old spec from git silently reverts them. Always export first.
- App Platform sends traffic to
http_port; an app listening on a different port fails health checks with no obvious error in the build log. - Static sites without
catchall_documentreturn 404 on client-side routes. - Droplet backups are taken on DigitalOcean’s schedule and stored with DigitalOcean; they do not replace application-level database dumps.
What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "DigitalOcean Apps"
short_description: "App specs in git, databases behind trusted sources"
default_prompt: "$digitalocean-apps Write a DigitalOcean App Platform spec for our API, worker and React frontend with Postgres" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.