Довідник DigitalOcean
Усі приклади використовують curl + python3 (без зовнішніх залежностей). Перед запуском задайте DO_TOKEN.
Сумісність з оболонками: в усіх аргументах python3 -c використано одинарні лапки, щоб ні bash, ні zsh не підставляли змінні. Код Python використовує лише подвійні лапки.
Як задати DO_TOKEN: у zsh запис DO_TOKEN=... curl ... в одному рядку НЕ працює: $DO_TOKEN підставляється до того, як призначення набуває чинності. Скористайтеся одним із варіантів:
export DO_TOKEN=... # then run snippets normally
DO_TOKEN=...; curl ... # semicolon separates assignment from command
Обробка помилок: усі приклади перевіряють наявність помилок DO API, перш ніж звертатися до даних. Помилки DO мають формат {"id": "...", "message": "..."}.
Розміри інстансів
Перегляд і фільтрація доступних розмірів:
curl -sS -H "Authorization: Bearer $DO_TOKEN" \
"https://api.digitalocean.com/v2/sizes?per_page=200" | python3 -c '
import json, sys
data = json.load(sys.stdin)
if "sizes" not in data:
print("Error:", data.get("message", data)); sys.exit(1)
region, vcpus, mem_gb = "nyc3", 2, 2
for s in sorted(data["sizes"], key=lambda x: (x["vcpus"], x["memory"], x["price_monthly"])):
if not s["available"] or region not in s["regions"]:
continue
if s["vcpus"] == vcpus and s["memory"] == mem_gb * 1024:
slug, vc, mem, disk, price = s["slug"], s["vcpus"], s["memory"]/1024, s["disk"], s["price_monthly"]
print(f"{slug:30s} {vc}vCPU {mem:.0f}GB {disk}GB ${price}/mo")
'
Змініть region, vcpus, mem_gb за потреби. Щоб шукати за мінімальними параметрами, використовуйте >= замість ==.
Пошук образів
Визначте slug найновішої Ubuntu LTS:
curl -sS -H "Authorization: Bearer $DO_TOKEN" \
"https://api.digitalocean.com/v2/images?type=distribution&per_page=200" | python3 -c '
import json, sys
data = json.load(sys.stdin)
if "images" not in data:
print("Error:", data.get("message", data)); sys.exit(1)
ubuntu = [i for i in data["images"] if i["distribution"] == "Ubuntu" and "LTS" in i.get("description", "")]
ubuntu.sort(key=lambda x: x["slug"], reverse=True)
for i in ubuntu[:5]:
slug, desc = i["slug"], i["description"]
print(f"{slug:30s} {desc}")
'
Пошук регіонів
curl -sS -H "Authorization: Bearer $DO_TOKEN" \
"https://api.digitalocean.com/v2/regions" | python3 -c '
import json, sys
data = json.load(sys.stdin)
if "regions" not in data:
print("Error:", data.get("message", data)); sys.exit(1)
for r in data["regions"]:
if r["available"]:
slug, name = r["slug"], r["name"]
print(f"{slug:10s} {name}")
'
Базовий профіль droplet
- Регіон:
nyc3 - Образ: найновіша Ubuntu LTS, доступна через API
- Тариф: Basic / Regular (Shared CPU)
- Приклад розміру: 2 vCPU / 4 GB RAM / 80 GB SSD / 4 TB трафіку (~$24/міс)
- Назва:
wlsly-<adjective>-<surname>(створена генератором імен нижче)
SSH-ключі
Перегляд ключів:
curl -sS -H "Authorization: Bearer $DO_TOKEN" \
"https://api.digitalocean.com/v2/account/keys?per_page=200" | python3 -c '
import json, sys
data = json.load(sys.stdin)
if "ssh_keys" not in data:
print("Error:", data.get("message", data)); sys.exit(1)
for k in data["ssh_keys"]:
kid, name, fp = k["id"], k["name"], k["fingerprint"]
print(f"{kid:>10} {name:30s} {fp}")
'
Завантаження нового ключа:
curl -sS -X POST \
-H "Authorization: Bearer $DO_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "name": "wlycli", "public_key": "'"$(cat ~/.ssh/wlsly_do.pub)"'" }' \
"https://api.digitalocean.com/v2/account/keys"
Проєкти
Перегляд проєктів:
curl -sS -H "Authorization: Bearer $DO_TOKEN" \
"https://api.digitalocean.com/v2/projects?per_page=200" | python3 -c '
import json, sys
data = json.load(sys.stdin)
if "projects" not in data:
print("Error:", data.get("message", data)); sys.exit(1)
for p in data["projects"]:
default = " (default)" if p.get("is_default") else ""
pid, name = p["id"], p["name"]
print(f"{pid} {name}{default}")
'
Додавання ресурсів до проєкту:
curl -sS -X POST \
-H "Authorization: Bearer $DO_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "resources": ["do:droplet:<droplet-id>", "do:dbaas:<db-id>"] }' \
"https://api.digitalocean.com/v2/projects/<project-id>/resources"
URN ресурсів проєкту
- Droplet:
do:droplet:<id> - Керована БД:
do:dbaas:<id>
Генератор імен
Створіть випадкову назву в стилі Docker з префіксом wlsly- для droplet і баз даних:
WLSLY_NAME=wlsly-$(shuf -n1 -e agile bold brave bright calm clever cool daring eager fair fast fierce fond frank gifted glad grand happy hardy keen kind lively lucid merry modest noble pensive proud quick quiet sharp shrewd sleek smart solid steady stoic sunny swift tender vivid warm wise witty zen)-$(shuf -n1 -e agnesi babbage bell bohr brahmagupta buck carson chatelet chebyshev curie darwin dijkstra easley einstein euler fermat feynman galois gauss goldberg goodall hamilton hawking hodgkin hopper hypatia jackson johnson kare keller knuth leavitt lovelace lumiere mahavira mandela mccarthy meitner mendel mirzakhani morse nash neumann nightingale noether northcutt panini pare pascal pasteur pike poincare ramanujan ride ritchie rosalind sagan shannon snyder swartz tesla thompson turing villani wescoff wing wozniak wright yalow)
echo "$WLSLY_NAME"
Використовуйте однакову назву і для droplet, і для його керованої бази даних (якщо її створено).
Створення droplet
curl -sS -X POST \
-H "Authorization: Bearer $DO_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name":"'"$WLSLY_NAME"'",
"region":"<region>",
"size":"<size-slug>",
"image":"<ubuntu-image-slug>",
"ssh_keys":["<fingerprint>"],
"monitoring":true,
"tags":["wlsly"]
}' \
"https://api.digitalocean.com/v2/droplets" | python3 -c '
import json, sys
data = json.load(sys.stdin)
if "droplet" not in data:
print("Error:", data.get("message", data)); sys.exit(1)
d = data["droplet"]
print("Droplet ID:", d["id"])
print("Name:", d["name"])
print("Status:", d["status"])
'
Очікування IP для droplet
IP-адреса в droplet з'явиться не одразу. Опитуйте, доки не буде status=active:
curl -sS -H "Authorization: Bearer $DO_TOKEN" \
"https://api.digitalocean.com/v2/droplets/<droplet-id>" | python3 -c '
import json, sys
data = json.load(sys.stdin)
if "droplet" not in data:
print("Error:", data.get("message", data)); sys.exit(1)
d = data["droplet"]
print("Status:", d["status"])
for net in d.get("networks", {}).get("v4", []):
print("IPv4 (%s): %s" % (net["type"], net["ip_address"]))
'
DNS (керований DigitalOcean)
Якщо DNS керується DigitalOcean, агент може створювати й оновлювати записи через API після того, як droplet отримає IP.
Перевірте, чи домен уже є в DO:
curl -sS -H "Authorization: Bearer $DO_TOKEN" \
"https://api.digitalocean.com/v2/domains/<domain>" | python3 -c '
import json, sys
data = json.load(sys.stdin)
if "domain" in data:
print("Domain: %s (exists)" % data["domain"]["name"])
else:
print("Domain not found - needs to be created")
if "message" in data:
print("API:", data["message"])
'
Створіть домен (лише один раз, якщо його не знайдено):
curl -sS -X POST \
-H "Authorization: Bearer $DO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"<domain>"}' \
"https://api.digitalocean.com/v2/domains"
Перегляньте наявні записи домену:
curl -sS -H "Authorization: Bearer $DO_TOKEN" \
"https://api.digitalocean.com/v2/domains/<domain>/records?per_page=200" | python3 -c '
import json, sys
data = json.load(sys.stdin)
if "domain_records" not in data:
print("Error:", data.get("message", data)); sys.exit(1)
for r in data["domain_records"]:
rid, rtype, name, rdata = r["id"], r["type"], r["name"], r["data"]
print(f"{rid:>10} {rtype:6s} {name:30s} {rdata}")
'
Створіть A-запис для кореневого домену (@):
curl -sS -X POST \
-H "Authorization: Bearer $DO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"A","name":"@","data":"<droplet-ip>","ttl":300}' \
"https://api.digitalocean.com/v2/domains/<domain>/records"
Створіть A-запис для піддомену storage (потрібен, якщо немає CDN):
curl -sS -X POST \
-H "Authorization: Bearer $DO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"A","name":"storage","data":"<droplet-ip>","ttl":300}' \
"https://api.digitalocean.com/v2/domains/<domain>/records"
Оновіть наявний запис (якщо запис уже існує):
curl -sS -X PUT \
-H "Authorization: Bearer $DO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"data":"<droplet-ip>"}' \
"https://api.digitalocean.com/v2/domains/<domain>/records/<record-id>"
Перед запуском certbot standalone переконайтеся, що A/AAAA-записи для <domain> (і storage.<domain>, якщо він використовується) вказують на IP droplet.
Прапорці CLI: --public-ip (замінює виявлений IP) і --skip-dns-preflight (пропускає перевірку).
Якщо для тимчасового налаштування потрібні самопідписані сертифікати, додайте --self-signed-certs (створює самопідписані сертифікати).
Нотатки про DNS
Якщо CDN не налаштовано, платформа використовує storage.<domain>. Створіть запис A/AAAA, що вказує на IP droplet, або CNAME на кореневий домен, якщо ваш DNS-провайдер це дозволяє.
Налаштування хоста (SSH)
Важливо: завжди використовуйте ssh host 'bash -s' << 'EOF' (heredoc у лапках), щоб виконувати багаторядкові скрипти віддалено. Ніколи не передавайте скрипти як аргументи ssh host "..." у подвійних лапках: локальна оболонка (особливо zsh) розкриє $(...), шаблони [...] і $VARIABLES до того, як надішле їх на віддалений хост.
ssh -o StrictHostKeyChecking=accept-new root@<droplet-ip> 'bash -s' << 'PROVISION'
set -euo pipefail
adduser --disabled-password --gecos "Welley Service" wlsly
usermod -aG sudo wlsly
echo "wlsly ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/wlsly
chmod 440 /etc/sudoers.d/wlsly
mkdir -p /home/wlsly/.ssh
cp /root/.ssh/authorized_keys /home/wlsly/.ssh/authorized_keys
chown -R wlsly:wlsly /home/wlsly/.ssh
chmod 700 /home/wlsly/.ssh
chmod 600 /home/wlsly/.ssh/authorized_keys
apt-get update -qq
apt-get install -y -qq ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" > /etc/apt/sources.list.d/docker.list
apt-get update -qq
apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
usermod -aG docker wlsly
echo "=== Verify ==="
docker --version
docker compose version
id wlsly
PROVISION
Потім перейдіть до користувача без прав root:
ssh -o StrictHostKeyChecking=accept-new wlsly@<droplet-ip> 'echo "Connected as $(whoami)"'
Керований Postgres (DigitalOcean)
Облікові дані стають відомі лише після створення. Спочатку створіть БД, потім прочитайте дані для підключення.
Створення:
curl -sS -X POST \
-H "Authorization: Bearer $DO_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name":"'"$WLSLY_NAME"'",
"engine":"pg",
"version":"16",
"region":"nyc3",
"size":"db-s-1vcpu-2gb",
"num_nodes":1
}' \
"https://api.digitalocean.com/v2/databases" | python3 -c '
import json, sys
data = json.load(sys.stdin)
if "database" not in data:
print("Error:", data.get("message", data)); sys.exit(1)
db = data["database"]
print("DB ID:", db["id"])
print("Status:", db["status"])
'
Опитуйте, доки статус не стане online (це займає 3-5 хвилин):
curl -sS -H "Authorization: Bearer $DO_TOKEN" \
"https://api.digitalocean.com/v2/databases/<db-id>" | python3 -c '
import json, sys
data = json.load(sys.stdin)
if "database" not in data:
print("Error:", data.get("message", data)); sys.exit(1)
db = data["database"]
print("Status:", db["status"])
if db["status"] == "online":
conn = db.get("connection", {})
priv = db.get("private_connection", {})
print("Host:", conn.get("host"))
print("Private host:", priv.get("host"))
print("Port:", conn.get("port"))
print("Database:", conn.get("database"))
print("User:", conn.get("user"))
'
Отримайте пароль користувача БД (пароль можна отримати лише з цієї кінцевої точки):
curl -sS -H "Authorization: Bearer $DO_TOKEN" \
"https://api.digitalocean.com/v2/databases/<db-id>/users" | python3 -c '
import json, sys
data = json.load(sys.stdin)
if "users" not in data:
print("Error:", data.get("message", data)); sys.exit(1)
for u in data["users"]:
print("User:", u["name"], "Password:", u.get("password", "N/A"), "Role:", u.get("role", "N/A"))
'
Передайте отримані хост, порт, назву бази даних, користувача і пароль через параметри:
--postgres-host, --postgres-port, --postgres-database, --postgres-user, --postgres-password.
Якщо пароля немає або термін його дії минув, скиньте автентифікацію користувача БД.
Розгортання через Welley CLI (приклади)
Базовий приклад із секретами:
wlycli deployment create \
--domain=example.com \
--email=admin@example.com \
--update-period=15min \
--env=stage \
--otel-endpoint=https://otlp-gateway-prod-us-east-2.grafana.net/otlp \
--otel-use-grpc=false \
--otel-headers='<OTEL_AUTH_HEADERS>' \
--smtp-host=email-smtp.us-east-2.amazonaws.com \
--smtp-user=<MAIL_USER> \
--smtp-password=<MAIL_PASSWORD> \
--smtp-from=noreply@mail.example.com \
--language-service=internal \
--translation-service=deepl \
--translation-service-api-key=<DEEPL_KEY> \
--with-sentry
Зовнішній Postgres:
wlycli deployment create \
--domain=example.com \
--email=admin@example.com \
--postgres-host=<DB_HOST> \
--postgres-port=<DB_PORT> \
--postgres-user=<DB_USER> \
--postgres-database=<DB_NAME> \
--postgres-password=<DB_PASSWORD>
Перед запуском certbot переконайтеся, що A/AAAA-записи DNS для <domain> (і storage.<domain>, якщо він використовується) вказують на IP droplet.