AWS Deployment Guide
Deploy SelfHostedDB on Amazon Web Services
This guide covers deploying SelfHostedDB on AWS using EC2, ECS, or EKS.
Option 1: AWS EC2 with Docker
Best for: Simple deployments, single instance
Steps
-
Launch EC2 Instance
- Use Amazon Linux 2 or Ubuntu 22.04 LTS
- Instance type: t3.small or larger (2 vCPU, 2GB RAM minimum)
- Security group: Allow inbound on port 3001 (or 80/443 with reverse proxy)
-
Install Docker on EC2
Amazon Linux 2:
sudo yum update -y sudo yum install docker -y sudo systemctl start docker sudo systemctl enable docker sudo usermod -aG docker ec2-userUbuntu:
sudo apt-get update sudo apt-get install docker.io docker-compose -y sudo systemctl start docker sudo systemctl enable docker sudo usermod -aG docker ubuntu -
Deploy Application
# Create directory for license data mkdir -p ~/selfhosteddb/license-data # Clone or pull Docker image docker pull your-registry/selfhosteddb:latest # Create .env file cat > .env << EOF DATABASE_URL=postgres://user:pass@your-rds-endpoint:5432/dbname?sslmode=require AUTH_USER=admin AUTH_PASS=your-strong-password-here PORT=3001 NODE_ENV=production LICENSE_SERVER_URL=https://license.selfhosteddb.com # Optional: Auto-activate license on startup # LICENSE_KEY=your-license-key-here # LICENSE_EMAIL=your@email.com EOF # Run container with volume mount for license persistence docker run -d \ --name selfhosteddb \ --restart unless-stopped \ -p 3001:3001 \ --env-file .env \ -v ~/selfhosteddb/license-data:/app/license-data \ your-registry/selfhosteddb:latest -
Activate License
Option A: Using CLI Tool (Recommended)
docker exec -it selfhosteddb activate-license \ --key "YOUR_LICENSE_KEY" \ --email "your@email.com"Option B: Using Environment Variables (Auto-activation)
If you set
LICENSE_KEYandLICENSE_EMAILin your.envfile, the license will activate automatically on container startup. Otherwise, use the CLI tool above. -
Set Up Reverse Proxy (Optional but Recommended)
# Install nginx sudo yum install nginx -y # or apt-get for Ubuntu # Configure nginx sudo nano /etc/nginx/conf.d/selfhosteddb.confserver { listen 80; server_name your-domain.com; location / { proxy_pass http://localhost:3001; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } }# Install Certbot for HTTPS sudo yum install certbot python3-certbot-nginx -y sudo certbot --nginx -d your-domain.com
Database Options
- AWS RDS PostgreSQL: Use RDS endpoint in
DATABASE_URL - Self-hosted on EC2: Run PostgreSQL container alongside app
- External: Point to existing PostgreSQL instance
Security Group Rules
- Inbound: Port 3001 (or 80/443) from your IP or load balancer
- Outbound: Port 5432 to RDS security group (if using RDS)
Option 2: AWS ECS (Elastic Container Service)
Best for: Scalable deployments, multiple instances
Steps
-
Push Image to ECR
# Create ECR repository aws ecr create-repository --repository-name selfhosteddb # Login to ECR aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin <account-id>.dkr.ecr.us-east-1.amazonaws.com # Tag and push docker tag selfhosteddb:latest <account-id>.dkr.ecr.us-east-1.amazonaws.com/selfhosteddb:latest docker push <account-id>.dkr.ecr.us-east-1.amazonaws.com/selfhosteddb:latest -
Store Secrets in AWS Secrets Manager
# Create secrets aws secretsmanager create-secret \ --name selfhosteddb/database-url \ --secret-string "postgres://user:pass@rds-endpoint:5432/dbname" aws secretsmanager create-secret \ --name selfhosteddb/app-credentials \ --secret-string '{"username":"admin","password":"secure-pass"}' # Optional: Store license for auto-activation aws secretsmanager create-secret \ --name selfhosteddb/license \ --secret-string '{"key":"your-license-key","email":"your@email.com"}' -
Create EFS File System for License Persistence
# Create EFS file system aws efs create-file-system \ --creation-token selfhosteddb-license \ --performance-mode generalPurpose \ --throughput-mode provisioned \ --provisioned-throughput-in-mibps 100 # Note the FileSystemId for use in task definition -
Create Task Definition
{ "family": "selfhosteddb", "networkMode": "awsvpc", "requiresCompatibilities": ["FARGATE"], "cpu": "512", "memory": "1024", "containerDefinitions": [{ "name": "selfhosteddb", "image": "<account-id>.dkr.ecr.us-east-1.amazonaws.com/selfhosteddb:latest", "portMappings": [{ "containerPort": 3001, "protocol": "tcp" }], "environment": [ {"name": "NODE_ENV", "value": "production"}, {"name": "PORT", "value": "3001"}, {"name": "LICENSE_SERVER_URL", "value": "https://license.selfhosteddb.com"} ], "secrets": [ { "name": "DATABASE_URL", "valueFrom": "arn:aws:secretsmanager:us-east-1:<account-id>:secret:selfhosteddb/database-url" }, { "name": "AUTH_USER", "valueFrom": "arn:aws:secretsmanager:us-east-1:<account-id>:secret:selfhosteddb/app-credentials:username::" }, { "name": "AUTH_PASS", "valueFrom": "arn:aws:secretsmanager:us-east-1:<account-id>:secret:selfhosteddb/app-credentials:password::" }, { "name": "LICENSE_KEY", "valueFrom": "arn:aws:secretsmanager:us-east-1:<account-id>:secret:selfhosteddb/license:key::" }, { "name": "LICENSE_EMAIL", "valueFrom": "arn:aws:secretsmanager:us-east-1:<account-id>:secret:selfhosteddb/license:email::" } ], "mountPoints": [{ "sourceVolume": "license-data", "containerPath": "/app/license-data" }], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/ecs/selfhosteddb", "awslogs-region": "us-east-1", "awslogs-stream-prefix": "ecs" } }, "healthCheck": { "command": ["CMD-SHELL", "curl -f http://localhost:3001/api/health || exit 1"], "interval": 30, "timeout": 5, "retries": 3, "startPeriod": 60 } }], "volumes": [{ "name": "license-data", "efsVolumeConfiguration": { "fileSystemId": "fs-xxxxxx", "transitEncryption": "ENABLED" } }] }Note: If you don't set
LICENSE_KEYandLICENSE_EMAILsecrets, you can activate the license manually using the CLI tool:# Get task ID aws ecs list-tasks --cluster your-cluster --service-name selfhosteddb # Execute CLI tool aws ecs execute-command \ --cluster your-cluster \ --task <task-id> \ --container selfhosteddb \ --command "activate-license --key YOUR_KEY --email your@email.com" \ --interactive -
Create ECS Service
aws ecs create-service \ --cluster your-cluster \ --service-name selfhosteddb \ --task-definition selfhosteddb:1 \ --desired-count 2 \ --launch-type FARGATE \ --network-configuration "awsvpcConfiguration={subnets=[subnet-xxx,subnet-yyy],securityGroups=[sg-xxx],assignPublicIp=ENABLED}" \ --load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:...,containerName=selfhosteddb,containerPort=3001"- Use Application Load Balancer (ALB) for HTTPS
- Configure health checks:
/api/health - Set desired count: 2+ for high availability
Best Practices
- Use AWS Secrets Manager for credentials (not environment variables)
- Enable CloudWatch Logs for monitoring
- Use ALB with SSL certificate (ACM) for HTTPS
- Configure auto-scaling based on CPU/memory
Option 3: AWS EKS (Kubernetes)
Best for: Enterprise deployments, complex orchestration needs
Kubernetes Deployment YAML
apiVersion: apps/v1
kind: Deployment
metadata:
name: selfhosteddb
spec:
replicas: 2
selector:
matchLabels:
app: selfhosteddb
template:
metadata:
labels:
app: selfhosteddb
spec:
containers:
- name: selfhosteddb
image: <account-id>.dkr.ecr.us-east-1.amazonaws.com/selfhosteddb:latest
ports:
- containerPort: 3001
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: selfhosteddb-secrets
key: database-url
- name: AUTH_USER
valueFrom:
secretKeyRef:
name: selfhosteddb-secrets
key: auth-user
- name: AUTH_PASS
valueFrom:
secretKeyRef:
name: selfhosteddb-secrets
key: auth-pass
- name: NODE_ENV
value: "production"
- name: PORT
value: "3001"
livenessProbe:
httpGet:
path: /api/health
port: 3001
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /api/health
port: 3001
initialDelaySeconds: 10
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: selfhosteddb-service
spec:
selector:
app: selfhosteddb
ports:
- protocol: TCP
port: 80
targetPort: 3001
type: LoadBalancerCreate Secrets
kubectl create secret generic selfhosteddb-secrets \
--from-literal=database-url='postgres://...' \
--from-literal=auth-user='admin' \
--from-literal=auth-pass='your-password' \
--from-literal=license-key='your-license-key' \
--from-literal=license-email='your@email.com'Create Persistent Volume for License Data
# Create PVC for license persistence
kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: license-data-pvc
namespace: default
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
EOFUpdate Deployment with License Configuration
Add these to your deployment YAML:
env:
- name: LICENSE_SERVER_URL
value: "https://license.selfhosteddb.com"
- name: LICENSE_KEY
valueFrom:
secretKeyRef:
name: selfhosteddb-secrets
key: license-key
- name: LICENSE_EMAIL
valueFrom:
secretKeyRef:
name: selfhosteddb-secrets
key: license-email
volumeMounts:
- name: license-data
mountPath: /app/license-data
volumes:
- name: license-data
persistentVolumeClaim:
claimName: license-data-pvcNote: If you don't set LICENSE_KEY and LICENSE_EMAIL in secrets, activate manually:
kubectl exec -it deployment/selfhosteddb -- activate-license \
--key "YOUR_LICENSE_KEY" \
--email "your@email.com"Monitoring
CloudWatch
- Logs: Configured automatically with ECS/EKS
- Metrics: CPU, memory, network usage
- Alarms: Set up for high CPU, memory, or error rates
Health Checks
Use /api/health endpoint for:
- ECS target group health checks
- EKS liveness/readiness probes
- CloudWatch alarms
Cost Optimization
- EC2: Use Reserved Instances for long-term deployments
- ECS: Use Fargate Spot for non-critical workloads
- EKS: Use managed node groups with auto-scaling
- RDS: Use Reserved Instances for database
Security Best Practices
- Use AWS Secrets Manager for all credentials
- Enable VPC for network isolation
- Use security groups to restrict access
- Enable RDS encryption at rest
- Use SSL/TLS for all database connections
Related Documentation
- Production Deployment Guide - General production deployment
- Security Best Practices - Security configuration
- Troubleshooting Guide - Common issues
- Installation Guide - Initial setup
Last Updated: 2025-01-27