DigitalOcean
intermediate
35-45 minutes

Droplet Deployment

Deploy applications on DigitalOcean Droplets - virtual machines with full control and flexibility.

What you'll accomplish

Your application running on a Droplet with proper configuration

Prerequisites

  • DigitalOcean account
  • doctl CLI installed
  • SSH key added to account

What are Droplets?

Droplets are DigitalOcean's virtual machines:

  • Full control - Root access, any software
  • SSD storage - Fast performance
  • Simple pricing - Fixed monthly cost
  • Quick deployment - Ready in 55 seconds

When to Use Droplets

Use CaseWhy Droplets
Custom softwareInstall anything
Legacy appsFull OS control
DatabasesSelf-managed options
DevelopmentSSH access, testing
Background jobsLong-running processes

Step 1: Create a Droplet

Via CLI

bash
# Basic Droplet
doctl compute droplet create myserver \
  --image ubuntu-22-04-x64 \
  --size s-1vcpu-1gb \
  --region nyc1 \
  --ssh-keys $(doctl compute ssh-key list --format ID --no-header | head -1)

# With multiple options
doctl compute droplet create myserver \
  --image ubuntu-22-04-x64 \
  --size s-2vcpu-2gb \
  --region nyc1 \
  --ssh-keys KEY_ID \
  --enable-monitoring \
  --enable-backups \
  --vpc-uuid VPC_ID \
  --tag-names "production,web"

Via Control Panel

  1. Go to Create → Droplets
  2. Choose region
  3. Select image (Ubuntu 22.04 recommended)
  4. Choose size
  5. Add SSH key
  6. Click Create Droplet

Droplet Sizes

SizevCPURAMStorageBandwidthPrice
s-1vcpu-512mb-10gb1512 MB10 GB500 GB$4/mo
s-1vcpu-1gb11 GB25 GB1 TB$6/mo
s-1vcpu-2gb12 GB50 GB2 TB$12/mo
s-2vcpu-2gb22 GB60 GB3 TB$18/mo
s-2vcpu-4gb24 GB80 GB4 TB$24/mo
s-4vcpu-8gb48 GB160 GB5 TB$48/mo

Available Images

bash
# List public images
doctl compute image list --public

# Common images:
# ubuntu-24-04-x64    - Ubuntu 24.04 LTS
# ubuntu-22-04-x64    - Ubuntu 22.04 LTS
# debian-12-x64       - Debian 12
# centos-stream-9-x64 - CentOS Stream 9
# docker-20-04        - Docker on Ubuntu 20.04
# nodejs              - Node.js pre-installed

Step 2: Connect via SSH

Get Droplet IP

bash
# List Droplets
doctl compute droplet list

# Get specific Droplet IP
doctl compute droplet get DROPLET_ID --format PublicIPv4 --no-header

SSH Connection

bash
# Connect via doctl
doctl compute ssh myserver

# Or standard SSH
ssh root@YOUR_DROPLET_IP

First Login

bash
# Update system
apt update && apt upgrade -y

# Create non-root user
adduser deploy
usermod -aG sudo deploy

# Set up SSH for new user
mkdir -p /home/deploy/.ssh
cp ~/.ssh/authorized_keys /home/deploy/.ssh/
chown -R deploy:deploy /home/deploy/.ssh

# Disable root login (optional but recommended)
sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
systemctl restart sshd

Step 3: Configure Firewall

Using UFW

bash
# Enable firewall
ufw default deny incoming
ufw default allow outgoing

# Allow SSH
ufw allow 22/tcp

# Allow HTTP/HTTPS
ufw allow 80/tcp
ufw allow 443/tcp

# Enable firewall
ufw enable

# Check status
ufw status

Using DigitalOcean Cloud Firewall

bash
# Create firewall
doctl compute firewall create \
  --name myapp-firewall \
  --inbound-rules "protocol:tcp,ports:22,address:0.0.0.0/0 protocol:tcp,ports:80,address:0.0.0.0/0 protocol:tcp,ports:443,address:0.0.0.0/0" \
  --outbound-rules "protocol:tcp,ports:all,address:0.0.0.0/0 protocol:udp,ports:all,address:0.0.0.0/0" \
  --droplet-ids DROPLET_ID

Step 4: Deploy Your Application

Node.js Application

bash
# Install Node.js
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt install -y nodejs

# Clone your app
cd /var/www
git clone https://github.com/username/myapp.git
cd myapp

# Install dependencies
npm ci --production

# Build (if needed)
npm run build

Create Systemd Service

bash
cat > /etc/systemd/system/myapp.service << 'EOF'
[Unit]
Description=My Node.js Application
After=network.target

[Service]
Type=simple
User=deploy
WorkingDirectory=/var/www/myapp
ExecStart=/usr/bin/node server.js
Restart=on-failure
RestartSec=10
Environment=NODE_ENV=production
Environment=PORT=3000

[Install]
WantedBy=multi-user.target
EOF

# Enable and start
systemctl daemon-reload
systemctl enable myapp
systemctl start myapp

# Check status
systemctl status myapp

Configure Nginx

bash
# Install Nginx
apt install -y nginx

# Configure reverse proxy
cat > /etc/nginx/sites-available/myapp << 'EOF'
server {
    listen 80;
    server_name example.com www.example.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}
EOF

# Enable site
ln -sf /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default

# Test and restart
nginx -t
systemctl restart nginx

Step 5: SSL with Let's Encrypt

bash
# Install Certbot
apt install -y certbot python3-certbot-nginx

# Get certificate
certbot --nginx -d example.com -d www.example.com

# Auto-renewal (already configured)
certbot renew --dry-run

Step 6: Docker Deployment

Install Docker

bash
# Install Docker
curl -fsSL https://get.docker.com | sh

# Add user to docker group
usermod -aG docker deploy

Run Container

bash
# Pull and run
docker pull ghcr.io/username/myapp:latest

docker run -d \
  --name myapp \
  -p 80:3000 \
  --restart unless-stopped \
  -e NODE_ENV=production \
  ghcr.io/username/myapp:latest

Docker Compose

yaml
# docker-compose.yml
version: '3.8'

services:
  web:
    image: ghcr.io/username/myapp:latest
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=${DATABASE_URL}
    restart: unless-stopped

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - /etc/letsencrypt:/etc/letsencrypt:ro
    depends_on:
      - web
    restart: unless-stopped

Step 7: User Data (Automation)

Automate setup with cloud-init:

bash
doctl compute droplet create myserver \
  --image ubuntu-22-04-x64 \
  --size s-1vcpu-1gb \
  --region nyc1 \
  --ssh-keys KEY_ID \
  --user-data-file ./setup.sh

Example setup.sh

bash
#!/bin/bash

# Update system
apt-get update
apt-get upgrade -y

# Install Node.js
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt-get install -y nodejs nginx

# Install app
cd /var/www
git clone https://github.com/username/myapp.git
cd myapp
npm ci --production

# Create systemd service
cat > /etc/systemd/system/myapp.service << 'SVCEOF'
[Unit]
Description=My Application
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/var/www/myapp
ExecStart=/usr/bin/node server.js
Restart=on-failure
Environment=NODE_ENV=production
Environment=PORT=3000

[Install]
WantedBy=multi-user.target
SVCEOF

systemctl enable myapp
systemctl start myapp

# Configure nginx
cat > /etc/nginx/sites-available/myapp << 'NGXEOF'
server {
    listen 80;
    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
    }
}
NGXEOF

ln -sf /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
systemctl restart nginx

Step 8: Monitoring

Enable Monitoring Agent

bash
# During creation
doctl compute droplet create myserver \
  --enable-monitoring

# Or install manually
curl -sSL https://repos.insights.digitalocean.com/install.sh | bash

View Metrics

  • Control Panel → Droplet → Graphs
  • CPU, Memory, Disk, Bandwidth

Set Up Alerts

  1. Control Panel → Monitoring → Create Alert
  2. Select metric (CPU, Memory, Disk)
  3. Set threshold
  4. Add notification channel

Step 9: Backups and Snapshots

Enable Backups

bash
# On creation
doctl compute droplet create myserver \
  --enable-backups

# Or enable for existing
# (via Control Panel: Droplet → Backups → Enable)

Backups:

  • Weekly automatic
  • 4-week retention
  • 20% of Droplet cost

Create Snapshot

bash
# Create snapshot
doctl compute droplet-action snapshot DROPLET_ID \
  --snapshot-name "myserver-$(date +%Y%m%d)"

# List snapshots
doctl compute snapshot list --resource droplet

Step 10: Scaling

Resize Droplet

bash
# Power off first
doctl compute droplet-action power-off DROPLET_ID

# Resize
doctl compute droplet-action resize DROPLET_ID --size s-2vcpu-4gb

# Power on
doctl compute droplet-action power-on DROPLET_ID

Horizontal Scaling

Create multiple Droplets with a Load Balancer:

bash
# Create Droplets
for i in 1 2 3; do
  doctl compute droplet create web-$i \
    --image ubuntu-22-04-x64 \
    --size s-1vcpu-1gb \
    --region nyc1 \
    --ssh-keys KEY_ID \
    --tag-names "web"
done

# Create Load Balancer
doctl compute load-balancer create \
  --name mylb \
  --region nyc1 \
  --tag-name web \
  --forwarding-rules "entry_protocol:http,entry_port:80,target_protocol:http,target_port:3000"

Troubleshooting

Can't SSH

bash
# Check Droplet is running
doctl compute droplet get DROPLET_ID

# Try with verbose
ssh -v root@IP

# Check firewall
doctl compute firewall list

Service Won't Start

bash
# Check logs
journalctl -u myapp.service -n 50

# Check service status
systemctl status myapp

# Try manual start
node /var/www/myapp/server.js

Out of Memory

bash
# Check memory
free -h

# Add swap
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
Note

Checkpoint: Create a Droplet, deploy your application, configure Nginx, and verify it's accessible via the public IP.