Production Deployment Guide
This guide covers best practices for deploying CloudflareD1.NET applications to production environments.
Environment Configuration
Local vs Remote Mode
CloudflareD1.NET supports two modes:
Local Mode (Development)
{
"CloudflareD1": {
"UseLocalMode": true,
"LocalDatabasePath": "myapp.db"
}
}
Remote Mode (Production)
{
"CloudflareD1": {
"UseLocalMode": false,
"AccountId": "your-account-id",
"DatabaseId": "your-database-id",
"ApiToken": "your-api-token"
}
}
Environment Variables
Use environment variables for production credentials:
Docker/Kubernetes
CloudflareD1__UseLocalMode=false
CloudflareD1__AccountId=xxx
CloudflareD1__DatabaseId=xxx
CloudflareD1__ApiToken=xxx
Azure App Service
CloudflareD1:UseLocalMode=false
CloudflareD1:AccountId=xxx
CloudflareD1:DatabaseId=xxx
CloudflareD1:ApiToken=xxx
AWS Lambda/ECS
CloudflareD1__UseLocalMode=false
CloudflareD1__AccountId=xxx
CloudflareD1__DatabaseId=xxx
CloudflareD1__ApiToken=xxx
Security Best Practices
API Token Management
1. Use Scoped Tokens
- Create API tokens with minimum required permissions
- Scope:
Account → D1 → Edit(or Read if read-only) - Never use Global API Keys in production
2. Rotate Tokens Regularly
# Create new token
# Update secrets management system
# Deploy with new token
# Revoke old token after verification
3. Use Secrets Management
Kubernetes Secrets
apiVersion: v1
kind: Secret
metadata:
name: cloudflare-d1-secrets
type: Opaque
stringData:
api-token: your-token-here
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cloudflare-d1-config
data:
account-id: "your-account-id"
database-id: "your-database-id"
Azure Key Vault
builder.Configuration.AddAzureKeyVault(
new Uri("https://your-vault.vault.azure.net/"),
new DefaultAzureCredential());
AWS Secrets Manager
builder.Configuration.AddSecretsManager(
configurator: options =>
{
options.SecretFilter = entry => entry.Name.StartsWith("CloudflareD1");
});
HashiCorp Vault
builder.Configuration.AddVault(options =>
{
options.ConfigureVaultClientSettings(settings =>
{
settings.VaultServerUriWithPort = "https://vault.example.com:8200";
});
});
Network Security
1. Use HTTPS Only
services.AddCloudflareD1(options =>
{
options.ApiBaseUrl = "https://api.cloudflare.com/client/v4/"; // Always HTTPS
});
2. Firewall Rules
- Allow outbound HTTPS (443) to
api.cloudflare.com - No inbound rules needed (client-initiated only)
3. Private Networks
// For internal APIs, consider API Gateway or reverse proxy
services.AddHttpClient<D1Client>()
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{
UseProxy = true,
Proxy = new WebProxy("http://your-proxy:8080")
});
Health Check Integration
ASP.NET Core Health Checks
// Startup.cs / Program.cs
builder.Services.AddHealthChecks()
.AddCheck("d1-database", async () =>
{
var d1Client = services.GetRequiredService<ID1Client>();
var health = await d1Client.CheckHealthAsync();
return health.IsHealthy
? HealthCheckResult.Healthy($"D1 connection healthy (Latency: {health.LatencyMs}ms)")
: HealthCheckResult.Unhealthy($"D1 connection failed: {health.ErrorMessage}");
});
app.MapHealthChecks("/health");
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
Predicate = _ => true
});
app.MapHealthChecks("/health/live", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("live")
});
Kubernetes Probes
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
containers:
- name: myapp
image: myapp:latest
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 2
Docker Health Check
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY . .
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
ENTRYPOINT ["dotnet", "MyApp.dll"]
Monitoring & Observability
Structured Logging
builder.Logging.AddConsole();
builder.Logging.AddApplicationInsights(); // Azure
// or
builder.Logging.AddAWSProvider(); // AWS CloudWatch
builder.Services.AddCloudflareD1(options =>
{
// Logging is automatic - queries log at Information level
// Enable Debug for detailed request/response logging
});
Example Log Output:
info: CloudflareD1.NET.D1Client[0]
D1 query executed successfully, returned 42 result(s) (Duration: 123ms)
warn: CloudflareD1.NET.Providers.CloudflareD1Provider[0]
D1 API request failed (Attempt 2/3): Rate limit exceeded. Retrying in 200ms...
info: CloudflareD1.NET.Providers.CloudflareD1Provider[0]
Health check completed: Healthy (Latency: 87.45ms)
Metrics Collection
// Custom metrics with Application Insights
public class D1MetricsCollector
{
private readonly TelemetryClient _telemetry;
public async Task<T> TrackD1Operation<T>(
string operationName,
Func<Task<T>> operation)
{
var sw = Stopwatch.StartNew();
try
{
var result = await operation();
_telemetry.TrackMetric($"D1.{operationName}.Duration", sw.ElapsedMilliseconds);
_telemetry.TrackMetric($"D1.{operationName}.Success", 1);
return result;
}
catch (Exception ex)
{
_telemetry.TrackMetric($"D1.{operationName}.Error", 1);
_telemetry.TrackException(ex);
throw;
}
}
}
Distributed Tracing
builder.Services.AddOpenTelemetry()
.WithTracing(tracing =>
{
tracing
.AddHttpClientInstrumentation()
.AddAspNetCoreInstrumentation()
.AddConsoleExporter(); // Or Jaeger, Zipkin, etc.
});
Performance Configuration
Retry Policy
services.AddCloudflareD1(options =>
{
options.EnableRetry = true; // Default: true
options.MaxRetries = 3; // Default: 3
options.InitialRetryDelayMs = 100; // Default: 100ms (exponential backoff)
});
Retry Behavior:
- Retries: 429 (rate limit), 503 (service unavailable), network errors, timeouts
- Exponential backoff: 100ms → 200ms → 400ms → 800ms
- Total max delay: ~1.5 seconds for 3 retries
Timeout Configuration
services.AddCloudflareD1(options =>
{
options.TimeoutSeconds = 30; // Default: 30 seconds
// Increase for long-running queries
// Decrease for strict latency requirements
});
Batch Operations
// Efficient bulk inserts
var statements = users.Select(u => new D1Statement
{
Sql = "INSERT INTO users (name, email) VALUES (?, ?)",
Params = new object[] { u.Name, u.Email }
}).ToList();
await client.BatchAsync(statements);
Connection Pooling (Local Mode)
// Local mode uses a single connection with locking
// For high concurrency, consider:
services.AddSingleton<ID1Client>(sp =>
{
var options = Options.Create(new D1Options
{
UseLocalMode = true,
LocalDatabasePath = "myapp.db"
});
return new D1Client(options, sp.GetRequiredService<ILogger<D1Client>>());
});
Deployment Checklist
Pre-Deployment
- All tests passing
- Migration scripts tested
- API tokens created with correct permissions
- Secrets stored in secrets management system
- Health check endpoints tested
- Logging configured
- Monitoring/alerting set up
- Load testing completed
Deployment
- Deploy with environment variables
- Verify health check responds
- Run smoke tests
- Check logs for errors
- Monitor initial traffic
Post-Deployment
- Verify metrics collection
- Test rollback procedure
- Document deployment notes
- Update runbook
High Availability Patterns
Multi-Region Deployment
// Use Cloudflare's global network
// D1 automatically replicates to edge locations
services.AddCloudflareD1(options =>
{
options.AccountId = "your-account";
options.DatabaseId = "your-database"; // Same database, global reach
});
Graceful Degradation
public class ResilientDataService
{
private readonly ID1Client _d1Client;
private readonly IMemoryCache _cache;
public async Task<User> GetUserAsync(int id)
{
// Try cache first
if (_cache.TryGetValue($"user:{id}", out User cachedUser))
return cachedUser;
try
{
// Try D1
var user = await _d1Client.Query<User>("users")
.Where(u => u.Id == id)
.FirstOrDefaultAsync();
// Cache on success
_cache.Set($"user:{id}", user, TimeSpan.FromMinutes(5));
return user;
}
catch (D1Exception ex)
{
_logger.LogError(ex, "D1 query failed, using fallback");
// Fallback logic here
throw;
}
}
}
Circuit Breaker Pattern
// Using Polly
services.AddHttpClient<D1Client>()
.AddTransientHttpErrorPolicy(policy =>
policy.CircuitBreakerAsync(
handledEventsAllowedBeforeBreaking: 5,
durationOfBreak: TimeSpan.FromSeconds(30)
));
Scaling Considerations
Read Replicas
- Cloudflare D1 automatically replicates globally
- Reads are served from nearest edge location
- No additional configuration needed
Write Scaling
- D1 uses SQLite, optimized for read-heavy workloads
- Consider caching for write-heavy scenarios
- Use batch operations for bulk writes
Rate Limits
- Monitor rate limit headers (if exposed by API)
- Implement client-side throttling if needed
- Use exponential backoff (enabled by default)
Container Deployment
Docker Compose
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- CloudflareD1__UseLocalMode=false
- CloudflareD1__AccountId=${CLOUDFLARE_ACCOUNT_ID}
- CloudflareD1__DatabaseId=${CLOUDFLARE_DATABASE_ID}
- CloudflareD1__ApiToken=${CLOUDFLARE_API_TOKEN}
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3
Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:latest
ports:
- containerPort: 8080
env:
- name: CloudflareD1__UseLocalMode
value: "false"
- name: CloudflareD1__AccountId
valueFrom:
configMapKeyRef:
name: cloudflare-d1-config
key: account-id
- name: CloudflareD1__DatabaseId
valueFrom:
configMapKeyRef:
name: cloudflare-d1-config
key: database-id
- name: CloudflareD1__ApiToken
valueFrom:
secretKeyRef:
name: cloudflare-d1-secrets
key: api-token
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
Best Practices Summary
✅ Use environment variables for configuration
✅ Enable retry policy for resilience
✅ Implement health checks for monitoring
✅ Use structured logging for observability
✅ Store secrets securely (never in code)
✅ Test deployment process before production
✅ Monitor query performance and latency
✅ Use batch operations for efficiency
✅ Implement graceful degradation for HA
✅ Document runbooks for operations team
Next Steps
- Troubleshooting Guide - Common errors and solutions
- Performance Tuning Guide - Optimization techniques