digitalocean Best Practices
This guide outlines the definitive best practices for developing and deploying applications on DigitalOcean. Adhering to these rules ensures your projects are secure, performant, scalable, and maintainable, leveraging DigitalOcean's managed services and cloud-native patterns.
1. VPC-Centric Networking (Security & Simplicity)
ALWAYS isolate your infrastructure within Virtual Private Clouds (VPCs). This is the cornerstone of secure and efficient communication between your services. Connect all compute resources (Droplets, Kubernetes nodes, Functions) and managed services (Databases) within the same VPC.
Connect Managed Databases via VPC: Use the private connection string and add the VPC's CIDR block as the only trusted source. This drastically reduces the attack surface and simplifies IP management.
❌ BAD: Public database connections, individual IP trusted sources.
-- Public connection string, exposed to the internet
psql "postgresql://doadmin:PASSWORD@db-public-endpoint.db.ondigitalocean.com:25060/defaultdb?sslmode=require"
✅ GOOD: Private VPC connection, CIDR block trusted source.
-- Private connection string, only accessible within the VPC
psql "postgresql://doadmin:PASSWORD@db-private-endpoint.db.ondigitalocean.com:25060/defaultdb?sslmode=require"
Configuration: In DigitalOcean Control Panel, for your database, add your VPC's CIDR (e.g., 10.108.0.0/20) to Trusted Sources.
All Compute in VPC: Ensure all Droplets, Kubernetes nodes, and Functions are launched within the same VPC as your managed services.
2. Microservices-First Architecture
Embrace a microservices architecture for agility, scalability, and resilience. DigitalOcean provides excellent platforms for this.
Managed Kubernetes (DOKS) for Orchestration: For complex, containerized applications requiring fine-grained control and high availability.
App Platform for Rapid CI/CD: For web apps, APIs, and static sites needing fast, automated deployments with integrated CI/CD. Ideal for AI-assisted development workflows (e.g., with Claude Code).
Premium CPU-Optimized Droplets: For high-performance, resource-intensive workloads that benefit from dedicated CPU.
❌ BAD: Monolithic application on a single Droplet.
# Dockerfile for a large, multi-service monolith
FROM node:18-alpine
WORKDIR /app
COPY . .
RUN npm install
EXPOSE 8080 5432 # Exposing database directly from app container
CMD ["npm", "start"]
✅ GOOD: Decoupled services, deployed to appropriate platforms.
# Dockerfile for a single microservice (e.g., API)
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
RUN npm run build # If applicable
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist # Or similar for compiled assets
EXPOSE 8080
CMD ["node", "dist/index.js"] # Or your main entry point
Deployment: Deploy the API to App Platform or DOKS, use a Managed PostgreSQL database.
3. Leverage Managed Services
ALWAYS prefer DigitalOcean's managed services over self-hosting when available. They provide automated backups, failover, scaling, and security patching, reducing operational overhead.
Managed Databases: Use PostgreSQL, MySQL, Redis, or MongoDB.
Spaces (Object Storage): For static assets, backups, and media files. Integrate with CDN for performance.
Container Registry: Store private Docker images securely.
Load Balancers: Distribute traffic and provide SSL termination.
❌ BAD: Self-hosting PostgreSQL on a Droplet.
# Manual setup, no automated failover or backups
sudo apt update && sudo apt install postgresql postgresql-contrib
# ... manual configuration, backup scripts, monitoring, etc.
✅ GOOD: Provision a Managed PostgreSQL Database.
resource "digitalocean_database_cluster" "primary_db" {
name = "my-app-db"
engine = "pg"
version = "15"
size = "db-s-2vcpu-4gb"
region = "nyc3"
node_count = 1 # Start with 1, add standby nodes for HA
vpc_uuid = digitalocean_vpc.main.id # Ensure VPC is defined
}
4. Disciplined Container Builds
Follow Docker's best practices to create small, secure, and efficient container images.
Multi-stage Builds: Separate build-time dependencies from runtime dependencies.
Minimal Base Images: Use alpine variants or other minimal images.
Trusted Base Images: Prefer Docker Official Images or Verified Publisher images.
.dockerignore: Exclude unnecessary files from the build context.
Rebuild Images Often: Ensure dependencies are up-to-date with security patches.
❌ BAD: Single-stage build with development tools, no .dockerignore.
FROM node:18 # Large base image
WORKDIR /app
COPY . . # Copies everything, including .git, node_modules (if present)
RUN npm install # Installs dev dependencies too
CMD ["npm", "start"]
✅ GOOD: Multi-stage build, minimal base, .dockerignore in place.
# .dockerignore example:
# node_modules
# .git
# .env
# Dockerfile
# README.md
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install --omit=dev # Only install production dependencies
COPY . .
RUN npm run build # Compile application if needed
# Production stage
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist # Copy compiled app
EXPOSE 8080
CMD ["node", "dist/index.js"]
5. Automated Workflows (CI/CD & IaC)
Automate everything possible. This reduces human error, increases deployment speed, and ensures consistency.
CI/CD Pipelines: Use DigitalOcean App Platform's built-in CI/CD or integrate with GitHub Actions for DOKS deployments.
Infrastructure as Code (IaC): Manage all DigitalOcean resources with Terraform. This ensures your infrastructure is version-controlled, reproducible, and auditable.
❌ BAD: Manual resource creation via Control Panel.
# Click-ops in the UI
# Create Droplet -> Add Database -> Configure Load Balancer...
✅ GOOD: Define infrastructure with Terraform.
# main.tf
resource "digitalocean_project" "my_project" {
name = "My Awesome Project"
description = "Infrastructure for my awesome app."
purpose = "Web Application"
}
resource "digitalocean_vpc" "main" {
name = "my-app-vpc"
region = "nyc3"
ip_range = "10.10.0.0/20"
}
resource "digitalocean_kubernetes_cluster" "main_cluster" {
name = "my-app-cluster"
region = "nyc3"
version = "1.28.2-do.0"
vpc_uuid = digitalocean_vpc.main.id
node_pool {
name = "worker-pool"
size = "s-2vcpu-4gb"
node_count = 2
}
}
6. Performance & Cost Optimization
Continuously monitor and optimize your resources.
Autoscaling: Implement autoscaling for Droplet pools and Kubernetes node pools to match demand and save costs.
Load Balancers: Use DigitalOcean Load Balancers to distribute traffic and handle SSL.
Regular Architecture Reviews: Leverage DigitalOcean's free Solutions Engineer reviews to identify inefficiencies and security gaps.
Monitor Usage & Set Alerts: Track CPU, memory, disk I/O, network traffic, and billing.
❌ BAD: Over-provisioned Droplets running at low utilization.
# Manually scaled up to a large Droplet "just in case"
digitalocean droplet create --size s-8vcpu-16gb --image ubuntu-22-04 --region nyc3 --name my-server
✅ GOOD: Use autoscaling groups or right-sized resources.
resource "digitalocean_kubernetes_node_pool" "autoscaling_pool" {
cluster_id = digitalocean_kubernetes_cluster.main_cluster.id
name = "autoscaled-workers"
size = "s-2vcpu-4gb"
min_nodes = 1
max_nodes = 5
auto_scale = true
}
7. Testing & Observability
Integrate testing into your CI/CD and ensure robust monitoring.
Automated Testing: Include unit, integration, and end-to-end tests in your CI pipeline.
Staging Environments: Deploy to dedicated staging environments (e.g., via App Platform's branch deployments) before production.
Logging & Monitoring: Centralize logs and use DigitalOcean Monitoring for metrics and alerts.
❌ BAD: Manual testing only, no dedicated staging environment.
# Deploy directly to production after local testing
git push origin main # Triggers production deploy
✅ GOOD: Automated tests, staging environment, and feature branches.
# .github/workflows/ci-cd.yml (simplified)
name: CI/CD Pipeline
on:
push:
branches:
- main
- feature/*
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build Docker Image
run: docker build -t my-app:${{ github.sha }} .
- name: Run Unit Tests
run: docker run my-app:${{ github.sha }} npm test
deploy-staging:
needs: build-and-test
if: startsWith(github.ref, 'refs/heads/feature/')
runs-on: ubuntu-latest
steps:
# ... deploy to App Platform staging environment
- name: Deploy to App Platform Staging
run: doctl apps create-deployment --app-id ${{ secrets.APP_PLATFORM_STAGING_ID }} --image my-app:${{ github.sha }}
deploy-production:
needs: build-and-test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
# ... deploy to App Platform production environment
- name: Deploy to App Platform Production
run: doctl apps create-deployment --app-id ${{ secrets.APP_PLATFORM_PROD_ID }} --image my-app:${{ github.sha }}
1---2name: digitalocean3description: "[Applies to: **/*] Definitive guidelines for building, deploying, and managing cloud-native applications on DigitalOcean, focusing on secure, scalable, and cost-efficient practices."4source: "cursor_mdc"5---67# digitalocean Best Practices89This guide outlines the definitive best practices for developing and deploying applications on DigitalOcean. Adhering to these rules ensures your projects are secure, performant, scalable, and maintainable, leveraging DigitalOcean's managed services and cloud-native patterns.1011## 1. VPC-Centric Networking (Security & Simplicity)1213**ALWAYS** isolate your infrastructure within Virtual Private Clouds (VPCs). This is the cornerstone of secure and efficient communication between your services. Connect all compute resources (Droplets, Kubernetes nodes, Functions) and managed services (Databases) within the same VPC.1415* **Connect Managed Databases via VPC**: Use the private connection string and add the VPC's CIDR block as the *only* trusted source. This drastically reduces the attack surface and simplifies IP management.1617 ❌ BAD: Public database connections, individual IP trusted sources.18 ```sql19 -- Public connection string, exposed to the internet20 psql "postgresql://doadmin:PASSWORD@db-public-endpoint.db.ondigitalocean.com:25060/defaultdb?sslmode=require"21 ```2223 ✅ GOOD: Private VPC connection, CIDR block trusted source.24 ```sql25 -- Private connection string, only accessible within the VPC26 psql "postgresql://doadmin:PASSWORD@db-private-endpoint.db.ondigitalocean.com:25060/defaultdb?sslmode=require"27 ```28 *Configuration*: In DigitalOcean Control Panel, for your database, add your VPC's CIDR (e.g., `10.108.0.0/20`) to Trusted Sources.2930* **All Compute in VPC**: Ensure all Droplets, Kubernetes nodes, and Functions are launched within the same VPC as your managed services.3132## 2. Microservices-First Architecture3334Embrace a microservices architecture for agility, scalability, and resilience. DigitalOcean provides excellent platforms for this.3536* **Managed Kubernetes (DOKS) for Orchestration**: For complex, containerized applications requiring fine-grained control and high availability.37* **App Platform for Rapid CI/CD**: For web apps, APIs, and static sites needing fast, automated deployments with integrated CI/CD. Ideal for AI-assisted development workflows (e.g., with Claude Code).38* **Premium CPU-Optimized Droplets**: For high-performance, resource-intensive workloads that benefit from dedicated CPU.3940 ❌ BAD: Monolithic application on a single Droplet.41 ```dockerfile42 # Dockerfile for a large, multi-service monolith43 FROM node:18-alpine44 WORKDIR /app45 COPY . .46 RUN npm install47 EXPOSE 8080 5432 # Exposing database directly from app container48 CMD ["npm", "start"]49 ```5051 ✅ GOOD: Decoupled services, deployed to appropriate platforms.52 ```dockerfile53 # Dockerfile for a single microservice (e.g., API)54 FROM node:20-alpine AS builder55 WORKDIR /app56 COPY package*.json ./57 RUN npm install --production58 COPY . .59 RUN npm run build # If applicable6061 FROM node:20-alpine62 WORKDIR /app63 COPY --from=builder /app/node_modules ./node_modules64 COPY --from=builder /app/dist ./dist # Or similar for compiled assets65 EXPOSE 808066 CMD ["node", "dist/index.js"] # Or your main entry point67 ```68 *Deployment*: Deploy the API to App Platform or DOKS, use a Managed PostgreSQL database.6970## 3. Leverage Managed Services7172**ALWAYS** prefer DigitalOcean's managed services over self-hosting when available. They provide automated backups, failover, scaling, and security patching, reducing operational overhead.7374* **Managed Databases**: Use PostgreSQL, MySQL, Redis, or MongoDB.75* **Spaces (Object Storage)**: For static assets, backups, and media files. Integrate with CDN for performance.76* **Container Registry**: Store private Docker images securely.77* **Load Balancers**: Distribute traffic and provide SSL termination.7879 ❌ BAD: Self-hosting PostgreSQL on a Droplet.80 ```bash81 # Manual setup, no automated failover or backups82 sudo apt update && sudo apt install postgresql postgresql-contrib83 # ... manual configuration, backup scripts, monitoring, etc.84 ```8586 ✅ GOOD: Provision a Managed PostgreSQL Database.87 ```terraform88 resource "digitalocean_database_cluster" "primary_db" {89 name = "my-app-db"90 engine = "pg"91 version = "15"92 size = "db-s-2vcpu-4gb"93 region = "nyc3"94 node_count = 1 # Start with 1, add standby nodes for HA95 vpc_uuid = digitalocean_vpc.main.id # Ensure VPC is defined96 }97 ```9899## 4. Disciplined Container Builds100101Follow Docker's best practices to create small, secure, and efficient container images.102103* **Multi-stage Builds**: Separate build-time dependencies from runtime dependencies.104* **Minimal Base Images**: Use `alpine` variants or other minimal images.105* **Trusted Base Images**: Prefer Docker Official Images or Verified Publisher images.106* **`.dockerignore`**: Exclude unnecessary files from the build context.107* **Rebuild Images Often**: Ensure dependencies are up-to-date with security patches.108109 ❌ BAD: Single-stage build with development tools, no `.dockerignore`.110 ```dockerfile111 FROM node:18 # Large base image112 WORKDIR /app113 COPY . . # Copies everything, including .git, node_modules (if present)114 RUN npm install # Installs dev dependencies too115 CMD ["npm", "start"]116 ```117118 ✅ GOOD: Multi-stage build, minimal base, `.dockerignore` in place.119 ```dockerfile120 # .dockerignore example:121 # node_modules122 # .git123 # .env124 # Dockerfile125 # README.md126127 # Build stage128 FROM node:20-alpine AS builder129 WORKDIR /app130 COPY package*.json ./131 RUN npm install --omit=dev # Only install production dependencies132 COPY . .133 RUN npm run build # Compile application if needed134135 # Production stage136 FROM node:20-alpine137 WORKDIR /app138 COPY --from=builder /app/node_modules ./node_modules139 COPY --from=builder /app/dist ./dist # Copy compiled app140 EXPOSE 8080141 CMD ["node", "dist/index.js"]142 ```143144## 5. Automated Workflows (CI/CD & IaC)145146Automate everything possible. This reduces human error, increases deployment speed, and ensures consistency.147148* **CI/CD Pipelines**: Use DigitalOcean App Platform's built-in CI/CD or integrate with GitHub Actions for DOKS deployments.149* **Infrastructure as Code (IaC)**: Manage all DigitalOcean resources with Terraform. This ensures your infrastructure is version-controlled, reproducible, and auditable.150151 ❌ BAD: Manual resource creation via Control Panel.152 ```bash153 # Click-ops in the UI154 # Create Droplet -> Add Database -> Configure Load Balancer...155 ```156157 ✅ GOOD: Define infrastructure with Terraform.158 ```terraform159 # main.tf160 resource "digitalocean_project" "my_project" {161 name = "My Awesome Project"162 description = "Infrastructure for my awesome app."163 purpose = "Web Application"164 }165166 resource "digitalocean_vpc" "main" {167 name = "my-app-vpc"168 region = "nyc3"169 ip_range = "10.10.0.0/20"170 }171172 resource "digitalocean_kubernetes_cluster" "main_cluster" {173 name = "my-app-cluster"174 region = "nyc3"175 version = "1.28.2-do.0"176 vpc_uuid = digitalocean_vpc.main.id177 node_pool {178 name = "worker-pool"179 size = "s-2vcpu-4gb"180 node_count = 2181 }182 }183 ```184185## 6. Performance & Cost Optimization186187Continuously monitor and optimize your resources.188189* **Autoscaling**: Implement autoscaling for Droplet pools and Kubernetes node pools to match demand and save costs.190* **Load Balancers**: Use DigitalOcean Load Balancers to distribute traffic and handle SSL.191* **Regular Architecture Reviews**: Leverage DigitalOcean's free Solutions Engineer reviews to identify inefficiencies and security gaps.192* **Monitor Usage & Set Alerts**: Track CPU, memory, disk I/O, network traffic, and billing.193194 ❌ BAD: Over-provisioned Droplets running at low utilization.195 ```bash196 # Manually scaled up to a large Droplet "just in case"197 digitalocean droplet create --size s-8vcpu-16gb --image ubuntu-22-04 --region nyc3 --name my-server198 ```199200 ✅ GOOD: Use autoscaling groups or right-sized resources.201 ```terraform202 resource "digitalocean_kubernetes_node_pool" "autoscaling_pool" {203 cluster_id = digitalocean_kubernetes_cluster.main_cluster.id204 name = "autoscaled-workers"205 size = "s-2vcpu-4gb"206 min_nodes = 1207 max_nodes = 5208 auto_scale = true209 }210 ```211212## 7. Testing & Observability213214Integrate testing into your CI/CD and ensure robust monitoring.215216* **Automated Testing**: Include unit, integration, and end-to-end tests in your CI pipeline.217* **Staging Environments**: Deploy to dedicated staging environments (e.g., via App Platform's branch deployments) before production.218* **Logging & Monitoring**: Centralize logs and use DigitalOcean Monitoring for metrics and alerts.219220 ❌ BAD: Manual testing only, no dedicated staging environment.221 ```bash222 # Deploy directly to production after local testing223 git push origin main # Triggers production deploy224 ```225226 ✅ GOOD: Automated tests, staging environment, and feature branches.227 ```yaml228 # .github/workflows/ci-cd.yml (simplified)229 name: CI/CD Pipeline230231 on:232 push:233 branches:234 - main235 - feature/*236237 jobs:238 build-and-test:239 runs-on: ubuntu-latest240 steps:241 - uses: actions/checkout@v3242 - name: Build Docker Image243 run: docker build -t my-app:${{ github.sha }} .244 - name: Run Unit Tests245 run: docker run my-app:${{ github.sha }} npm test246247 deploy-staging:248 needs: build-and-test249 if: startsWith(github.ref, 'refs/heads/feature/')250 runs-on: ubuntu-latest251 steps:252 # ... deploy to App Platform staging environment253 - name: Deploy to App Platform Staging254 run: doctl apps create-deployment --app-id ${{ secrets.APP_PLATFORM_STAGING_ID }} --image my-app:${{ github.sha }}255256 deploy-production:257 needs: build-and-test258 if: github.ref == 'refs/heads/main'259 runs-on: ubuntu-latest260 steps:261 # ... deploy to App Platform production environment262 - name: Deploy to App Platform Production263 run: doctl apps create-deployment --app-id ${{ secrets.APP_PLATFORM_PROD_ID }} --image my-app:${{ github.sha }}264 ```