Self-Hosting Next.js with AWS EC2 and DokPloy: A Complete Guide
A complete step-by-step guide to self-hosting your Next.js applications on AWS EC2 using DokPloy — with push-to-deploy from GitHub, automatic SSL certificates, and full control over your infrastructure.
Self-Hosting Next.js Apps with AWS EC2 and DokPloy
A complete step-by-step guide to self-hosting your Next.js applications on AWS EC2 using DokPloy — with push-to-deploy from GitHub, automatic SSL certificates, and full control over your infrastructure.
What You'll Get
By the end of this guide, you'll have:
- ✅ An AWS EC2 instance running Ubuntu
- ✅ DokPloy managing all your deployments via a clean dashboard
- ✅ Automatic deployments triggered by GitHub pushes
- ✅ Free SSL certificates via Traefik + Let's Encrypt
- ✅ Multiple Next.js apps running on a single instance
- ✅ An Elastic IP (static public IP) so your server address never changes
- ✅ Properly configured Security Groups (firewall rules)
- ✅ Cost: $0 for 12 months (Free Tier) → ~$15–$30/month after
Why AWS EC2?
| Reason | Detail |
|---|---|
| 🆓 Free Tier | 750 hours/month of t2.micro free for 12 months (new accounts) |
| 🌍 Global Regions | 30+ regions — deploy close to your users anywhere in the world |
| 🔒 Mature Security | IAM, Security Groups, VPC, KMS — enterprise-grade by default |
| 🔌 Ecosystem | Seamlessly connect RDS, S3, CloudFront, Route 53, and more |
| 📈 Scalable | Resize your instance or add load balancing with a few clicks |
AWS EC2 Instance Types for DokPloy
| Instance | vCPU | RAM | On-Demand/mo | Best For |
|---|---|---|---|---|
t2.micro 🆓 | 1 vCPU | 1 GB | Free (12 mo) | Testing / learning only |
t3.small | 2 vCPU | 2 GB | ~$15/mo | 1–2 lightweight apps |
t3.medium ★ | 2 vCPU | 4 GB | ~$30/mo | 2–4 apps + database |
t3.large | 2 vCPU | 8 GB | ~$61/mo | Heavy workloads |
t3.xlarge | 4 vCPU | 16 GB | ~$121/mo | Enterprise / many apps |
★ Recommended for production:
t3.mediumgives you 4 GB RAM — the minimum comfortable size for running DokPloy, a Next.js app, and a database simultaneously.⚠️ Free Tier note:
t2.micro(1 GB RAM) can run DokPloy but will struggle with Next.js builds. Use it only for learning. Switch tot3.mediumfor real projects.
Additional AWS Costs to Be Aware Of
Unlike simple VPS providers, AWS charges for several components separately:
| Component | Cost |
|---|---|
| EC2 instance (t3.medium) | |
| EBS storage (30 GB gp3) | ~$2.40/mo |
| Public IPv4 address | $0.005/hr (~$3.60/mo) ⚠️ |
| Data transfer out (first 100 GB) | Free |
| Data transfer out (after 100 GB) | $0.09/GB |
| Estimated total (t3.medium) | ~$36/mo |
⚠️ AWS has charged for public IPv4 addresses since February 1, 2024 — $0.005/hour per address whether your instance is running or not. Always use an Elastic IP (1 free per running instance) instead of auto-assigned IPs to avoid paying for multiple addresses.
Prerequisites
- An AWS account (free to create at aws.amazon.com)
- Basic terminal / command line knowledge
- A GitHub account with your Next.js project
- A domain name (optional but required for SSL)
Step 1: Setting Up Your AWS Account
1.1 Create an AWS Account
- Visit aws.amazon.com and click "Create an AWS Account"
- Enter your email address and choose an account name
- Add a payment method (required even for Free Tier)
- Verify your phone number
- Choose the "Basic support – Free" plan
✅ New accounts automatically get 12 months of Free Tier benefits, including 750 hours/month of
t2.micro.
1.2 Set Up AWS Billing Alerts (Important!)
Before doing anything else, set up a billing alarm so you're never surprised by unexpected charges.
- Go to AWS Console → Billing → Budgets
- Click "Create a budget"
- Choose "Zero spend budget" — this alerts you at the first cent of spend
- Or set a monthly threshold, e.g.
$10 - Enter your email for alerts
Step 2: Launch an EC2 Instance
2.1 Navigate to EC2
- Log in to the AWS Management Console
- In the search bar, type "EC2" and click the service
- Select your preferred region from the top-right dropdown (choose one close to your users)
2.2 Launch a New Instance
- Click "Launch Instance"
- Give your instance a name:
dokploy-server
2.3 Choose an Amazon Machine Image (AMI)
- Under "Application and OS Images", click "Browse more AMIs" or use Quick Start
- Select Ubuntu Server 24.04 LTS (HVM) — 64-bit x86
- Confirm it shows "Free tier eligible" if you're on a new account
Always use Ubuntu 22.04 LTS or 24.04 LTS for DokPloy. Avoid Amazon Linux — it has package differences that can complicate Docker setup.
2.4 Choose an Instance Type
Select based on your needs:
| Goal | Instance |
|---|---|
| Learning / testing | t2.micro (Free Tier) |
| Single production app | t3.small |
| Multiple apps + DB | t3.medium ★ |
Click "Next" after selecting.
2.5 Create a Key Pair (SSH Access)
- Under "Key pair (login)", click "Create new key pair"
- Configure:
- Name:
dokploy-key - Key pair type: RSA
- Private key format:
.pem(Mac/Linux) or.ppk(Windows/PuTTY)
- Name:
- Click "Create key pair" — the
.pemfile downloads automatically - Move it to a safe location and set permissions:
mv ~/Downloads/dokploy-key.pem ~/.ssh/
chmod 400 ~/.ssh/dokploy-key.pem⚠️ You cannot download this file again. If you lose it, you'll need to create a new key pair and replace it on the instance.
2.6 Configure Security Group (Firewall)
This is the most important step for DokPloy to work correctly. Create a new security group:
Security Group Name: dokploy-sg
Add the following Inbound Rules:
| Type | Protocol | Port Range | Source | Purpose |
|---|---|---|---|---|
| SSH | TCP | 22 | My IP | Secure SSH access |
| HTTP | TCP | 80 | 0.0.0.0/0 | Web traffic + SSL verification |
| HTTPS | TCP | 443 | 0.0.0.0/0 | Secure web traffic |
| Custom TCP | TCP | 3000 | 0.0.0.0/0 | DokPloy dashboard |
🔒 For SSH, select "My IP" instead of
0.0.0.0/0— this restricts SSH access to only your current IP address.
2.7 Configure Storage
- Under "Configure storage", set:
- Size:
30 GB(minimum recommended; Free Tier includes 30 GB) - Volume type:
gp3(faster and same price as gp2)
- Size:
2.8 Launch the Instance
- Review your configuration in the Summary panel on the right
- Click "Launch instance"
- Wait ~1 minute for the instance to show "Running" status
Step 3: Allocate an Elastic IP
An Elastic IP is a static public IP that stays assigned to your account even if the instance is stopped or restarted. Without it, your instance gets a new IP every time it restarts.
3.1 Allocate a New Elastic IP
- In the EC2 sidebar, go to "Network & Security" → "Elastic IPs"
- Click "Allocate Elastic IP address"
- Leave defaults (Amazon's pool of IPv4 addresses)
- Click "Allocate"
3.2 Associate the Elastic IP with Your Instance
- Select the newly allocated Elastic IP
- Click "Actions" → "Associate Elastic IP address"
- Under "Instance", select your
dokploy-server - Click "Associate"
✅ Your server now has a permanent public IP. Note this IP — you'll use it throughout this guide.
⚠️ An Elastic IP is free while associated with a running instance. You're only charged if it's allocated but unassociated. Always release Elastic IPs you're not using.
Step 4: Connect to Your Instance via SSH
4.1 Find Your Public IP
In the EC2 dashboard, select your instance and note the Elastic IP (or Public IPv4 address).
4.2 SSH Into the Server
ssh -i ~/.ssh/dokploy-key.pem ubuntu@YOUR_ELASTIC_IPType yes when prompted to accept the fingerprint.
Note: AWS Ubuntu instances use the
ubuntuuser, notroot.
4.3 Switch to Root (Optional)
DokPloy's install script works best as root:
sudo -i4.4 Update the System
apt update && apt upgrade -yStep 5: Install DokPloy
5.1 Run the Installer
curl -fsSL https://dokploy.com/install.sh | shThis will:
- Install Docker and Docker Compose
- Set up Traefik as a reverse proxy (handles SSL automatically)
- Configure and start the DokPloy dashboard
- Take approximately 1–3 minutes
5.2 Access the DokPloy Dashboard
Open your browser and visit:
http://YOUR_ELASTIC_IP:3000
If you can't reach it, double-check your Security Group has port 3000 open for inbound traffic.
Create your admin account:
- Choose a strong username
- Set a secure password
5.3 Enable Two-Factor Authentication (Recommended)
- Go to Settings → Security
- Enable 2FA using an authenticator app (Authy, Google Authenticator, 1Password, etc.)
Step 6: Connect GitHub to DokPloy
6.1 Create a GitHub App
- In DokPloy, go to Settings → Git
- Click "Connect GitHub"
- Click "Create GitHub App"
- Choose a globally unique name:
dokploy-yourusername-2024
6.2 Install the GitHub App on Your Repositories
- Click "Install" after creating the app
- Choose your GitHub account or organization
- Select "All repositories" or pick specific ones
- Complete the authorization flow
6.3 Verify the Connection
Your GitHub repositories should now appear in DokPloy's repository dropdowns. If they're not showing, wait 30 seconds and refresh the page.
Step 7: Deploy Your Next.js Application
7.1 Prepare Your Project
If you don't have a Next.js project yet:
pnpm create next-app@latest my-ec2-app
cd my-ec2-app
Push to GitHub:
git init
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/yourusername/my-ec2-app.git
git push -u origin main7.2 Create a Project in DokPloy
- Click "Create Project"
- Name:
my-nextjs-app - Click "Create"
7.3 Add an Application Service
- Inside the project, click "Add a Service"
- Select "Application"
- Name:
next-frontend - Click "Create"
7.4 Configure the Application
Build Settings:
| Field | Value |
|---|---|
| Repository | Your GitHub repo |
| Branch | main |
| Build Path | / |
| Build Pack | Next |
| Port | 3000 |
Domain:
- Click "Generate" for a temporary test subdomain, or
- Enter your custom domain:
https://myapp.yourdomain.com
Environment Variables (if needed):
NODE_ENV=production
NEXT_PUBLIC_API_URL=https://api.yourdomain.com
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb7.5 Deploy
- Click "Save"
- Click "Deploy"
- Monitor the build logs in real time
- Wait for "Deployment successful" (~1–3 minutes)
7.6 Test Push-to-Deploy
# App Router
cat > app/page.tsx << 'EOF'
export default function Home() {
return <h1>Hello from AWS EC2 + DokPloy! 🚀</h1>;
}
EOF
git add .
git commit -m "Test push-to-deploy"
git push origin mainDokPloy will detect the push via GitHub webhook and trigger a new deployment automatically. Check the "Deployments" tab for a webhook-initiated build.
Step 8: Configure a Custom Domain and SSL
8.1 Update Your DNS Records
In your DNS provider (Route 53, Cloudflare, Namecheap, etc.), add:
| Record Type | Host | Value |
|---|---|---|
| A | @ | YOUR_ELASTIC_IP |
| A | www | YOUR_ELASTIC_IP |
| A | * | YOUR_ELASTIC_IP |
DNS propagation typically takes 5–60 minutes.
8.2 Using AWS Route 53 (Optional)
If your domain is registered on Route 53 or you've transferred it there:
- Go to Route 53 → Hosted Zones
- Select your domain
- Click "Create Record"
- Choose A record, enter your Elastic IP
- Save
8.3 Using Cloudflare (Recommended for Extra Security)
- Add your domain to Cloudflare
- Update your registrar's nameservers to Cloudflare's
- Add an A record pointing to your Elastic IP
- Enable "Proxied" mode (orange cloud) for DDoS protection and CDN
8.4 Set Custom Domain in DokPloy
- Open your application in DokPloy
- Under "Domains", replace the generated URL with:
https://myapp.yourdomain.com
- Save — Traefik will automatically request and renew an SSL certificate via Let's Encrypt
✅ Your app is now live on HTTPS with a valid certificate. Zero manual SSL configuration needed.
Step 9: Add a Database (PostgreSQL)
9.1 Deploy PostgreSQL in DokPloy
- Inside your project, click "Add a Service"
- Select "Database"
- Choose PostgreSQL
- Configure:
- Name:
postgres-db - Database:
myapp - Username:
dbuser - Password: Use a strong generated password
- Name:
- Click "Create"
9.2 Connect Your App to the Database
Add the connection string to your app's environment variables in DokPloy:
DATABASE_URL=postgresql://dbuser:yourpassword@postgres-db:5432/myappDokPloy services on the same project communicate using their service name as the hostname (e.g.,
postgres-db), notlocalhost.
9.3 Using AWS RDS Instead (Optional)
For production workloads, you may prefer Amazon RDS (managed PostgreSQL):
- Go to AWS Console → RDS → Create database
- Choose PostgreSQL, Free Tier (
db.t3.micro) - Place it in the same VPC as your EC2 instance
- Configure the Security Group to allow port
5432from your EC2 instance's security group - Use the RDS endpoint as your
DATABASE_URL
RDS adds ~$15–25/mo but provides automated backups, multi-AZ failover, and managed updates.
Step 10: Monitor Your Instance
10.1 DokPloy Built-in Monitoring
DokPloy's Monitoring tab shows:
- Per-container CPU and memory usage
- Network I/O
- Disk usage
10.2 AWS CloudWatch (Built-in)
AWS automatically collects EC2 metrics in CloudWatch:
- Go to EC2 → Instances → Select instance → Monitoring tab
- View CPU utilization, network in/out, disk read/write
Set up a CloudWatch alarm for high CPU:
- Go to CloudWatch → Alarms → Create Alarm
- Select EC2 → Per-Instance Metrics → CPUUtilization
- Set threshold:
> 80%for5 consecutive minutes - Send notification to your email via SNS
10.3 SSH-Based Monitoring
ssh -i ~/.ssh/dokploy-key.pem ubuntu@YOUR_ELASTIC_IP
# Real-time CPU and memory
htop
# Disk usage
df -h
# Docker containers
docker ps
# Logs for a specific container
docker logs <container_id> --tail 100 -f
# Resource usage per container
docker stats
# DokPloy logs
docker logs dokploy
# Traefik logs
docker logs traefikTroubleshooting
Cannot Connect to DokPloy Dashboard (Port 3000)
Symptom: Browser times out at http://YOUR_IP:3000
- Verify the EC2 instance is Running in the console
- Check your Security Group — inbound rule for port 3000 must allow
0.0.0.0/0 - Confirm DokPloy container is running:
sudo docker ps | grep dokploy- If not running, restart it:
sudo docker restart dokploySSH Permission Denied
Symptom: ssh: Permission denied (publickey)
# Check key permissions (must be 400)
ls -la ~/.ssh/dokploy-key.pem
chmod 400 ~/.ssh/dokploy-key.pem
# Use the correct username (ubuntu, not root)
ssh -i ~/.ssh/dokploy-key.pem ubuntu@YOUR_ELASTIC_IP
# Verbose output for debugging
ssh -v -i ~/.ssh/dokploy-key.pem ubuntu@YOUR_ELASTIC_IPSSL Certificate Not Generating
Symptom: HTTPS shows a certificate warning
- Ensure DNS A records are pointing to your Elastic IP
- Wait for DNS propagation (up to 1 hour)
- Confirm ports 80 and 443 are open in the Security Group
- Let's Encrypt requires port 80 to be reachable for the HTTP challenge
- Check Traefik logs:
docker logs traefik
Deployment Build Failing
Symptom: Build errors in the DokPloy deployment logs
- Check the exact error in the deployment logs
- Ensure
package.jsonhas valid build scripts:
{
"scripts": {
"build": "next build",
"start": "next start"
}
}- Verify all environment variables are set in DokPloy
- Check available disk space:
df -h - If RAM is the issue (
t2.micro), consider upgrading tot3.medium
GitHub Webhook Not Triggering Auto-Deploy
Symptom: Pushing to GitHub doesn't trigger a new deployment
- Check Settings → Webhooks in your GitHub repository — the DokPloy webhook URL should be listed
- Click on the webhook and check "Recent Deliveries" for errors
- Ensure the server is reachable on port 3000 from the internet
- Try a manual deploy first to verify the configuration works
- Re-create the GitHub App connection in DokPloy if needed
Instance Running Out of Memory (t2.micro)
Symptom: Build crashes, containers restart, SSH is slow
The t2.micro (1 GB RAM) is too small for Next.js builds. Options:
Option 1: Add swap space (temporary fix)
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstabOption 2: Upgrade instance type (recommended)
- Stop the instance in AWS Console
- Go to Actions → Instance Settings → Change Instance Type
- Select
t3.medium - Start the instance
High AWS Bill — Unexpected Charges
Common culprits:
- Unassociated Elastic IPs — release any not attached to a running instance
- Stopped instance still running — EC2 only stops billing for compute when instance is stopped, but EBS and Elastic IP keep accruing
- Data transfer — large amounts of outbound traffic cost $0.09/GB after 100 GB
- Multiple public IPv4 addresses — each costs $0.005/hr
To audit: Go to AWS Cost Explorer → Service breakdown → EC2-Other to see EBS and IP charges.
Cost Breakdown
EC2 On-Demand (us-east-1 / N. Virginia)
| Component | Cost |
|---|---|
| t3.medium instance | ~$30.37/mo |
| 30 GB gp3 EBS storage | ~$2.40/mo |
| 1 Elastic IP (running) | Free |
| Data transfer (first 100 GB out) | Free |
| Total (on-demand) | ~$33/mo |
Savings Options
| Option | Savings | Best For |
|---|---|---|
| On-Demand | Baseline | Flexible / unpredictable usage |
| 1-Year Reserved (no upfront) | ~30% | Committed projects |
| 1-Year Reserved (partial upfront) | ~40% | Stable production apps |
| 3-Year Reserved | ~60% | Long-term infrastructure |
| Spot Instances | ~70–90% | Non-critical / interruptible workloads |
💡 For a stable production deployment, a 1-year reserved
t3.mediumbrings the cost down to ~$18–20/mo — comparable to Contabo while staying on AWS infrastructure.
Comparison
| Platform | Monthly Cost |
|---|---|
| AWS EC2 t3.medium + DokPloy | ~$33/mo (on-demand) |
| AWS EC2 t3.medium (1-yr reserved) | ~$20/mo |
| Contabo Cloud VPS 20 + DokPloy | ~$8/mo |
| Vercel Pro | $20/mo + usage |
| Railway | $5–20+/mo |
| Render | $7–25+/mo |
AWS costs more than Contabo, but offers a global infrastructure, deeper ecosystem integration (RDS, S3, CloudFront, Route 53), and enterprise-grade SLAs.
AWS-Specific Extras
Connect to S3 for File Storage
Store user uploads in S3 instead of the server disk:
- Create an S3 bucket in the AWS Console
- Create an IAM user with
AmazonS3FullAccesspolicy - Generate access keys for the IAM user
- Add to DokPloy environment variables:
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_REGION=us-east-1
AWS_S3_BUCKET=your-bucket-nameUse CloudFront as a CDN
- Go to CloudFront → Create Distribution
- Set your domain or EC2 Elastic IP as the origin
- Configure caching rules for static assets
- Point your domain's CNAME to the CloudFront distribution URL
This offloads static file serving, reduces EC2 bandwidth costs, and improves global load times.
Automatic Backups with AWS Backup
- Go to AWS Backup → Create Backup Plan
- Select your EC2 instance and EBS volumes
- Schedule daily snapshots (e.g., keep last 7 days)
- Cost: ~$0.05/GB per snapshot per month
Security Hardening
Restrict SSH Access
Update the Security Group to only allow SSH from your IP:
- Go to EC2 → Security Groups → dokploy-sg
- Edit inbound rules for port 22
- Change source from
0.0.0.0/0to "My IP"
Re-run this whenever your IP changes.
Install fail2ban
sudo apt install fail2ban -y
sudo systemctl enable fail2ban
sudo systemctl start fail2banThis automatically blocks IPs that repeatedly fail SSH login attempts.
Configure UFW Firewall
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 3000/tcp
sudo ufw enableDisable Root SSH Login
sudo nano /etc/ssh/sshd_config
# Set: PermitRootLogin no
# Set: PasswordAuthentication no
sudo systemctl restart sshdScaling Your Setup
Vertical Scaling (Resize Instance)
- Stop the instance
- Go to Actions → Instance Settings → Change Instance Type
- Choose a larger type (e.g.,
t3.large) - Start the instance — your Elastic IP and EBS volume stay attached
Downtime is typically under 2 minutes.
Horizontal Scaling with a Load Balancer
For high-availability setups:
- Create an Application Load Balancer (ALB) in EC2
- Launch 2+ EC2 instances with DokPloy
- Register instances in a Target Group
- Point your domain to the ALB DNS name
- Use Amazon RDS for a shared database across instances
DokPloy supports Docker Swarm for orchestrating containers across multiple nodes from a single dashboard.
Quick Reference
Essential Commands
# SSH into server
ssh -i ~/.ssh/dokploy-key.pem ubuntu@YOUR_ELASTIC_IP
# Switch to root
sudo -i
# View all Docker containers
docker ps
# View DokPloy logs
docker logs dokploy --tail 50 -f
# View Traefik logs
docker logs traefik --tail 50 -f
# Restart DokPloy
docker restart dokploy
# Check disk usage
df -h
# Check memory
free -h
# Real-time resource monitor
htop
# Update server
apt update && apt upgrade -yAWS Console Quick Links
| Service | URL |
|---|---|
| EC2 Instances | console.aws.amazon.com/ec2/v2/home#Instances |
| Elastic IPs | console.aws.amazon.com/ec2/v2/home#Addresses |
| Security Groups | console.aws.amazon.com/ec2/v2/home#SecurityGroups |
| CloudWatch | console.aws.amazon.com/cloudwatch |
| Billing | console.aws.amazon.com/billing |
| Cost Explorer | console.aws.amazon.com/cost-management/home |
DokPloy Dashboard
| Page | URL |
|---|---|
| Dashboard | http://YOUR_ELASTIC_IP:3000 |
| After domain setup | https://dokploy.yourdomain.com |
Next Steps
After completing this guide, consider:
- Move DokPloy dashboard to a subdomain — set
dokploy.yourdomain.comas the dashboard domain with SSL - Set up automated EC2 snapshots — use AWS Backup for daily EBS snapshots
- Use RDS for databases — more reliable and easier to manage than self-hosted Postgres on EC2
- Add CloudFront — serve static assets faster and reduce EC2 bandwidth costs
- Configure a staging environment — deploy a
stagingbranch to a separate subdomain automatically - Set up CloudWatch alarms — get notified of high CPU, memory, or disk usage
- Enable AWS GuardDuty — intelligent threat detection for your AWS account
Conclusion
You now have a production-grade Next.js deployment pipeline running on AWS EC2 with DokPloy. Compared to managed platforms, this gives you:
- 🚀 Push-to-deploy from GitHub with zero configuration
- 🔒 Automatic SSL certificates via Traefik + Let's Encrypt
- 🌍 AWS global infrastructure with 30+ regions
- 🔌 Deep AWS ecosystem integration — RDS, S3, CloudFront, Route 53
- 📈 Flexible scaling from
t3.smalltoc6i.4xlargewith no migration - 🛡️ Enterprise security — IAM, Security Groups, VPC, CloudTrail
The key AWS-specific concepts to master are: Security Groups (your firewall), Elastic IPs (static addressing), EBS (your disk), and IAM (access control). Once you understand those, AWS becomes a very powerful and flexible foundation for any application.
Built with ❤️ — Adapted from the original Hetzner + DokPloy guide. Updated for AWS EC2.