A Droplet is DigitalOcean's virtual machine: a slice of a physical host with its own kernel, a local SSD, a public and a private network interface, and root access for you. It is the simplest compute product on the platform and the one with the fewest guard rails. Managed products such as App Platform hide the operating system; a Droplet hands it to you, which means patching, firewalls, backups and secrets are your job.

This article explains how Droplets are built and billed, then provisions one properly: from code, with cloud-init, inside a VPC, behind a Cloud Firewall attached by tag, with data on a volume and backups you have tested. It covers the metadata service and why secrets do not belong in user-data, a worked layout for a small web service, the failure modes that catch teams, and when to choose something more managed. Prices change and are not quoted; the billing rules below come from DigitalOcean's documentation.

What a Droplet is

Under the hood a Droplet is a Linux virtual machine on DigitalOcean's virtualised hardware. The plan you choose fixes vCPUs, memory, local disk and a transfer allowance. The first decision is shared versus dedicated CPU: Basic plans share physical cores with other tenants, which is cheap and fine for bursty web and development workloads, while the other families give you dedicated threads with predictable performance.

FamilyCPURange in the docsGood for
BasicShared1-8 vCPUs, 1-32 GB RAMSmall web apps, dev, low steady load
General PurposeDedicated2-48 vCPUs, 8-240 GB RAMBalanced production services
CPU-OptimizedDedicated2-48 vCPUs, 4-120 GB RAMBuilds, encoding, compute-heavy APIs
Memory-OptimizedDedicated2-32 vCPUs, 16-384 GB RAMCaches, in-memory analytics, databases
Storage-OptimizedDedicated2-32 vCPUs, 16-384 GB RAM, larger SSDLocal-disk databases, search indexes

GPU Droplets exist as a separate line with on-demand, spot and contract options; check the current documentation for models and regions rather than relying on a blog. The key operational fact is that the local disk lives and dies with the Droplet. Anything you cannot lose goes on a Volume, a managed database or object storage.

Architecture of a small production service

A small production service on Droplets usually looks like this.

A production Droplet layout in one regionInternetusers, adminsCloud Firewallnetwork layer, deny unmatchedLoad Balanceror Reserved IP443VPC (private network, region-scoped)Droplet web-1app, local SSDDroplet web-2app, local SSDDroplet db-1private onlyprivate IPVolumeblock storage, /dataMetadata 169.254.169.254id, region, IPs, user-data: per DropletBackupsweekly or dailySnapshotson demandFirewall rules attach by tag, so new Droplets inherit them at creation
Traffic passes a network-layer Cloud Firewall, then a Load Balancer or Reserved IP, to app Droplets in a VPC; the database Droplet has no public exposure and keeps data on a Volume. Backups and snapshots copy Droplet disks; each Droplet reads its own metadata from a link-local address.

The pieces each solve one problem. The VPC gives Droplets in a region private addresses, so the app talks to the database without crossing the public internet; for cross-VPC traffic see VPC peering. Private Droplets can be created with no public interface at all, which suits databases and workers. Cloud Firewalls are a free, stateful firewall that stops traffic at the network layer before it reaches the Droplet, and traffic that matches no rule is denied. Reserved IPs are static addresses you can move between Droplets, the simplest form of failover. Load Balancers spread traffic over several Droplets and health-check them; the general pattern is covered in cloud load balancers. Tags tie it together: a firewall that targets the tag web applies to every Droplet created with that tag.

Billing rules that change how you operate

DigitalOcean's billing documentation sets out rules that shape how you operate Droplets:

  • Droplets are billed per second, with a minimum charge of 60 seconds or $0.01, whichever is higher.
  • Bundled-plan CPU Droplets are capped at 672 hours (28 days) of usage per month, so a Droplet that runs all month costs its listed monthly price, and the hourly rate is effectively that price divided by 672.
  • Droplets on the newer v5 configuration have no monthly cap; the bill follows the actual hours in the calendar month.
  • Powered-off Droplets are still billed, because their compute stays reserved on the hypervisor. The same applies to GPU Droplets.
  • Basic backups are charged as a percentage of the Droplet's cost: 20% for weekly, 30% for daily. Usage-based backup plans price per GiB and offer intervals down to every 4 hours.

The practical consequence is that powering off saves nothing. To stop paying for an idle machine, snapshot it and destroy it, then recreate it from the snapshot later, accepting that the snapshot has its own storage charge and the public IP will change unless you use a Reserved IP. Per-second billing makes short-lived Droplets cheap: a CI runner or a load-test fleet that exists for twenty minutes costs twenty minutes. Outbound transfer beyond the allowance is billed too, which matters for media-heavy services; the general traps are in cloud egress costs.

Provisioning from code with cloud-init

Click-built servers drift and cannot be rebuilt in a hurry. Create Droplets from code, with configuration applied at first boot by cloud-init. DigitalOcean passes user-data to cloud-init, which runs it once on first boot. A minimal hardening file:

#cloud-config
users:
  - name: deploy
    groups: sudo
    shell: /bin/bash
    sudo: "ALL=(ALL) NOPASSWD:ALL"
    ssh_authorized_keys:
      - ssh-ed25519 AAAA...your-public-key deploy@laptop
ssh_pwauth: false
disable_root: true
package_update: true
package_upgrade: true
packages: [unattended-upgrades, fail2ban]
runcmd:
  - systemctl enable --now unattended-upgrades
  - mkdir -p /data

Then create the Droplet with doctl. The flags below come from the doctl reference; look up the current image slug with doctl compute image list-distribution and the VPC UUID with doctl vpcs list.

doctl compute droplet create web-1 \
  --region ams3 \
  --size s-2vcpu-2gb \
  --image <ubuntu-lts-slug> \
  --ssh-keys <key-fingerprint> \
  --vpc-uuid <vpc-uuid> \
  --tag-names web,prod \
  --user-data-file cloud-init.yaml \
  --enable-monitoring \
  --enable-backups \
  --wait

--enable-monitoring installs the DigitalOcean agent for memory and disk metrics that the hypervisor cannot see; --wait blocks until the Droplet is active, which keeps scripts honest. For more than a handful of servers, express the same resources in Terraform with the DigitalOcean provider, so firewalls, tags and volumes are reviewed as code.

Network: Cloud Firewalls and VPC

Lock the network down before the application is installed. A Cloud Firewall attached by tag:

doctl compute firewall create --name web-prod \
  --tag-names web \
  --inbound-rules "protocol:tcp,ports:443,address:0.0.0.0/0 protocol:tcp,ports:22,address:203.0.113.0/24" \
  --outbound-rules "protocol:tcp,ports:443,address:0.0.0.0/0 protocol:tcp,ports:80,address:0.0.0.0/0 protocol:udp,ports:53,address:0.0.0.0/0"

Inbound HTTPS is open to the world, SSH only to an admin range (replace the documentation range 203.0.113.0/24 with yours, or use a bastion or VPN). Because unmatched traffic is denied, a firewall with no outbound rules blocks all egress, including package updates and DNS, so define outbound rules on purpose. If your Droplets use IPv6, add equivalent IPv6 rules. Keep a host firewall such as ufw as a second layer; the Cloud Firewall protects against mistakes on the host, and the host firewall protects against mistakes in the Cloud Firewall. The database Droplet gets its own firewall that allows its port only from the tag web, or is created as a Private Droplet with no public interface.

The metadata service and secrets

Every Droplet can query its own metadata at the link-local address http://169.254.169.254/metadata/v1/. The index lists fields such as id, hostname, user-data, vendor-data, public-keys, region, interfaces and dns, and the service is reachable only from inside the Droplet. Scripts use it to discover themselves:

curl -s http://169.254.169.254/metadata/v1/id
curl -s http://169.254.169.254/metadata/v1/region
curl -s http://169.254.169.254/metadata/v1/interfaces/private/0/ipv4/address
curl -s http://169.254.169.254/metadata/v1/user-data

The last line is the warning. Any process on the Droplet, including a compromised web application, can read user-data, and so can an attacker who finds a server-side request forgery bug that makes your app fetch arbitrary URLs. So never put database passwords or API tokens in user-data. Use user-data only to install an agent that fetches secrets from a secret manager with a scoped credential, or inject secrets through your deployment pipeline. On hosts that run untrusted or internet-facing code, consider blocking the metadata address for the application user with an owner-match firewall rule, and make your HTTP clients refuse link-local destinations.

Storage, backups and restores

Separate the operating system from the data. Attach a Volume (block storage) to /data and keep databases and uploads there; volumes can be detached and moved to a replacement Droplet in the same region, and resized independently. Then layer the copies:

  • Backups are scheduled whole-Droplet disk images: weekly or daily on the basic backup plan, more often on usage-based plans. A disk image of a running database is not guaranteed to be application-consistent, so do not rely on it alone.
  • Snapshots are on-demand images you take before risky changes or to retire a Droplet cheaply.
  • Application-level dumps such as pg_dump shipped to object storage in another region give you a consistent, portable copy and protect against losing a whole region or account.

A backup you have not restored is a hope. Once a quarter, create a Droplet from the latest backup, attach a copy of the data, start the service and check a known record. Time it; that number is your real recovery time.

Worked example: a booking API

A team runs a small booking API: a Python app, PostgreSQL, about 30 requests per second at peak. On Droplets they deploy two app Droplets on a Basic plan tagged web, behind a Load Balancer, and one General Purpose Droplet for PostgreSQL, created with no public interface and with its data on a Volume. The web firewall allows 443 from anywhere and 22 from the office range; the database firewall allows 5432 only from the tag web. Daily backups on the database Droplet add 30% to its cost; weekly backups on the app Droplets add 20%, though they are rebuilt from code anyway, so the team later drops them. A nightly pg_dump goes to object storage in a second region.

Deployments rebuild rather than patch: a new Droplet is created from code, health-checked, added to the load balancer, and the old one destroyed. Per-second billing makes the overlap nearly free. When a kernel vulnerability lands, unattended-upgrades handles the package and a rolling rebuild handles the reboot. The one stateful machine, the database, is the riskiest; when the team outgrows its maintenance, moving to a managed database is the natural next step, as moving the app to App Platform would remove the rest of the operating-system work.

Failure modes

  • Paying for powered-off machines. Off is still billed; snapshot and destroy instead.
  • Secrets in user-data. Readable from the metadata endpoint by any local process and through SSRF.
  • SSH open to the world with passwords. Disable password login in cloud-init and restrict port 22 by firewall.
  • Empty outbound rules. A Cloud Firewall with no outbound rules blocks updates and DNS.
  • Data on the local disk. Destroying or rebuilding the Droplet loses it; use Volumes and dumps.
  • Untested backups. A disk image of a busy database may not restore cleanly; prove the restore.
  • Shared-CPU surprises. Steady heavy load on a Basic plan can see variable performance; move steady workloads to dedicated CPU.

Trade-offs

Droplets give you full control, predictable bills and a short learning curve, and they cost you all the operating-system work. App Platform removes that work for stateless web apps at the price of less control. Managed Kubernetes suits teams already running many containers. Compared with a hyperscaler VM such as an Azure VM, a Droplet has fewer instance types, simpler networking and less identity and policy tooling, which is a feature for small teams and a limit for regulated or very large ones. Choose Droplets when you want a Linux box you understand completely, and invest in rebuilding from code so the box is disposable.

What to do next

  1. Write a cloud-init file that creates a sudo user with your SSH key, disables root and password login, and enables automatic security updates.
  2. Create Droplets with doctl or Terraform, never by hand, with tags that match your firewall policy.
  3. Attach Cloud Firewalls by tag; restrict SSH to an admin range and write explicit outbound rules.
  4. Put databases on Private Droplets or behind a firewall that admits only the app tag, with data on a Volume.
  5. Move every secret out of user-data and into a secret manager or your deploy pipeline.
  6. Enable backups where state lives, add an off-region application dump, and restore it once this quarter.
  7. Audit for powered-off Droplets and snapshot-and-destroy the ones you do not need running.
Key takeaway: A Droplet is a Linux virtual machine you fully operate. Pick shared CPU for bursty work and dedicated CPU for steady load, remember that powered-off Droplets are still billed, create every Droplet from code with cloud-init, attach Cloud Firewalls by tag with explicit outbound rules, keep secrets out of user-data because the metadata service exposes it, keep data on Volumes, and prove your backups by restoring them.