Provisioning is fully automated. This page documents exactly what runs on your behalf so you can audit the process, understand timing expectations, and know where to look when something stalls.
When you click Provision (or run nself cloud create), the control plane kicks off this pipeline. Each step is visible in real-time on the server detail page.
We call the Hetzner Cloud API to create a VPS in your chosen region. Image: Ubuntu 24.04 LTS. Two SSH keys are injected: your personal key (if you uploaded one) and the ɳSelf control plane key (used for automated updates and support access). The Hetzner API typically acknowledges the creation request within 5 seconds.
# The VPS is created with these specs for each plan:
# Starter → CX11: 1 vCPU, 2 GB RAM, 20 GB NVMe
# Pro → CX21: 2 vCPU, 4 GB RAM, 40 GB NVMe
# Business → CX31: 2 vCPU, 8 GB RAM, 80 GB NVMe
# MAX → CX41+: 4+ vCPU, 16+ GB RAM, 160+ GB NVMeWe poll the Hetzner API every 5 seconds until the server reports running and its IP address is available. This typically takes 30–60 seconds. During this time Hetzner is allocating hardware and booting the base image.
A cloud-init script runs on first boot. It:
nself init to initialize the project directory.env.secretsunattended-upgradesThis step takes the most time: 3–5 minutes depending on network conditions in the region. You can watch cloud-init progress by SSH-ing in and tailing /var/log/cloud-init-output.log.
We use the Cloudflare API to create:
myapp.cloud.nself.org → <server IP>*.myapp.cloud.nself.org → <server IP>Cloudflare DNS propagates globally in seconds via anycast. No TTL wait required. If you brought a custom domain, you must have already pointed your CNAME at myapp.cloud.nself.org before provisioning — we verify the CNAME exists before proceeding.
We run certbot against Let's Encrypt using DNS-01 challenge (not HTTP-01). This issues a wildcard certificate that covers:
myapp.cloud.nself.org*.myapp.cloud.nself.org (covers api, auth, storage, admin, console)DNS-01 challenge requires Cloudflare API access, which we have for thecloud.nself.org zone. For custom domains, we require you to add a_acme-challenge CNAME to our Cloudflare-managed validation subdomain. Instructions appear in the dashboard during provisioning.
Certificates are stored on the VPS and renewed automatically 30 days before expiry. Renewal failures trigger an alert sent to your registered email.
Pro plugins are pulled from the ɳSelf plugin registry at ping.nself.org, verified against their signed manifests, and baked into the compose file. Your ɳSelf+ license key (included in every Cloud plan) is used for the registry auth check.
# The plugin install sequence looks like:
nself license set <your-cloud-license-key>
nself plugin install ai mux claw realtime auth # your selected plugins
nself build # generates docker-compose.ymlPlugin installation takes about 10 seconds per plugin. If you selected every plugin bundle, expect 3–5 minutes for this step alone.
We poll your public domain's /health endpoint every 10 seconds, up to 30 attempts (5 minutes). A successful health check confirms:
/graphql/auth/healthzIf the health check times out, the provisioning job is marked failed and you are not charged. You will see which service failed in the dashboard logs.
Once health check passes, the control plane sends your welcome email. It contains:
nself cloud use myapp| Step | Typical time | Maximum |
|---|---|---|
| creating_server | 10–30 s | 2 min |
| waiting_for_server | 30–60 s | 3 min |
| bootstrapping | 3–5 min | 8 min |
| creating_dns | 5–15 s | 1 min |
| configuring_ssl | 30–60 s | 3 min |
| installing_plugins | 10–30 s per plugin | 5 min (all bundles) |
| health_check | 15–45 s | 5 min |
| sending_welcome | <5 s | 1 min |
| Total (default stack) | 5–8 min | 12 min (all plugins) |
The dashboard shows the exact step that failed and the error message from the control plane. Common causes:
| Error | Cause | Fix |
|---|---|---|
| Hetzner capacity error at creating_server | Region at capacity (rare) | Pick a different region or retry in 5 min |
| Subdomain already taken | Name conflict | Pick a different subdomain |
| cloud-init timeout at bootstrapping | Transient apt/network issue | Click Retry — runs from the failed step |
| CNAME not found at configuring_ssl | Custom domain DNS not set up | Add CNAME record, then retry |
| Registry 503 at installing_plugins | Transient registry hiccup | Click Retry |
| health_check timeout | Service start race condition | Click Retry — usually clears in 30 s |
Retrying resumes from the failed step — it does not re-run the whole pipeline. If retries do not resolve the issue, open a support ticket. Provisioning failures are never billed.
Backup jobs are set up automatically during bootstrapping:
pg_dump of Postgres and a MinIO bucket export are pushed to off-site object storage in a second Hetzner region within the same legal jurisdiction. Retained for 7 days.Restore is triggered from Dashboard → Server → Backups → Restore. The stack goes offline briefly during restore (under 5 minutes for most sizes).
If you want to reset your instance to a clean state, you can delete and re-create it. Before deleting, export your data:
# Export before reprovisioning
nself cloud exec myapp -- nself db backup
nself cloud backup create myapp # trigger on-demand backup
nself cloud backup list myapp # confirm backup is there
# Destroy and re-create (destructive!)
nself cloud delete myapp --force
nself cloud create myapp --plan pro --region fsn1The provisioning job exposes a JSON status endpoint you can poll programmatically:
# Poll provisioning status
curl -H "Authorization: Bearer $NSELF_CLOUD_TOKEN" \
https://api.cloud.nself.org/v1/instances/myapp/provision
# Response:
{
"instance": "myapp",
"status": "installing_plugins",
"step": 6,
"total_steps": 8,
"started_at": "2026-05-07T10:32:01Z",
"elapsed_seconds": 287,
"current_step_started_at": "2026-05-07T10:36:12Z"
}nself cloud commands