Command Palette

Search for a command to run...

Blog
PreviousNext

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?

ReasonDetail
🆓 Free Tier750 hours/month of t2.micro free for 12 months (new accounts)
🌍 Global Regions30+ regions — deploy close to your users anywhere in the world
🔒 Mature SecurityIAM, Security Groups, VPC, KMS — enterprise-grade by default
🔌 EcosystemSeamlessly connect RDS, S3, CloudFront, Route 53, and more
📈 ScalableResize your instance or add load balancing with a few clicks

AWS EC2 Instance Types for DokPloy

InstancevCPURAMOn-Demand/moBest For
t2.micro 🆓1 vCPU1 GBFree (12 mo)Testing / learning only
t3.small2 vCPU2 GB~$15/mo1–2 lightweight apps
t3.medium2 vCPU4 GB~$30/mo2–4 apps + database
t3.large2 vCPU8 GB~$61/moHeavy workloads
t3.xlarge4 vCPU16 GB~$121/moEnterprise / many apps

Recommended for production: t3.medium gives 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 to t3.medium for real projects.


Additional AWS Costs to Be Aware Of

Unlike simple VPS providers, AWS charges for several components separately:

ComponentCost
EC2 instance (t3.medium)$0.0416/hr ($30/mo)
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

  1. Visit aws.amazon.com and click "Create an AWS Account"
  2. Enter your email address and choose an account name
  3. Add a payment method (required even for Free Tier)
  4. Verify your phone number
  5. 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.

  1. Go to AWS Console → Billing → Budgets
  2. Click "Create a budget"
  3. Choose "Zero spend budget" — this alerts you at the first cent of spend
  4. Or set a monthly threshold, e.g. $10
  5. Enter your email for alerts

Step 2: Launch an EC2 Instance

2.1 Navigate to EC2

  1. Log in to the AWS Management Console
  2. In the search bar, type "EC2" and click the service
  3. Select your preferred region from the top-right dropdown (choose one close to your users)

2.2 Launch a New Instance

  1. Click "Launch Instance"
  2. Give your instance a name: dokploy-server

2.3 Choose an Amazon Machine Image (AMI)

  1. Under "Application and OS Images", click "Browse more AMIs" or use Quick Start
  2. Select Ubuntu Server 24.04 LTS (HVM) — 64-bit x86
  3. 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:

GoalInstance
Learning / testingt2.micro (Free Tier)
Single production appt3.small
Multiple apps + DBt3.medium

Click "Next" after selecting.


2.5 Create a Key Pair (SSH Access)

  1. Under "Key pair (login)", click "Create new key pair"
  2. Configure:
    • Name: dokploy-key
    • Key pair type: RSA
    • Private key format: .pem (Mac/Linux) or .ppk (Windows/PuTTY)
  3. Click "Create key pair" — the .pem file downloads automatically
  4. 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:

TypeProtocolPort RangeSourcePurpose
SSHTCP22My IPSecure SSH access
HTTPTCP800.0.0.0/0Web traffic + SSL verification
HTTPSTCP4430.0.0.0/0Secure web traffic
Custom TCPTCP30000.0.0.0/0DokPloy 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

  1. Under "Configure storage", set:
    • Size: 30 GB (minimum recommended; Free Tier includes 30 GB)
    • Volume type: gp3 (faster and same price as gp2)

2.8 Launch the Instance

  1. Review your configuration in the Summary panel on the right
  2. Click "Launch instance"
  3. 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

  1. In the EC2 sidebar, go to "Network & Security" → "Elastic IPs"
  2. Click "Allocate Elastic IP address"
  3. Leave defaults (Amazon's pool of IPv4 addresses)
  4. Click "Allocate"

3.2 Associate the Elastic IP with Your Instance

  1. Select the newly allocated Elastic IP
  2. Click "Actions" → "Associate Elastic IP address"
  3. Under "Instance", select your dokploy-server
  4. 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_IP

Type yes when prompted to accept the fingerprint.

Note: AWS Ubuntu instances use the ubuntu user, not root.


4.3 Switch to Root (Optional)

DokPloy's install script works best as root:

sudo -i

4.4 Update the System

apt update && apt upgrade -y

Step 5: Install DokPloy

5.1 Run the Installer

curl -fsSL https://dokploy.com/install.sh | sh

This 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

  1. Go to Settings → Security
  2. Enable 2FA using an authenticator app (Authy, Google Authenticator, 1Password, etc.)

Step 6: Connect GitHub to DokPloy

6.1 Create a GitHub App

  1. In DokPloy, go to Settings → Git
  2. Click "Connect GitHub"
  3. Click "Create GitHub App"
  4. Choose a globally unique name:
dokploy-yourusername-2024

6.2 Install the GitHub App on Your Repositories

  1. Click "Install" after creating the app
  2. Choose your GitHub account or organization
  3. Select "All repositories" or pick specific ones
  4. 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 main

7.2 Create a Project in DokPloy

  1. Click "Create Project"
  2. Name: my-nextjs-app
  3. Click "Create"

7.3 Add an Application Service

  1. Inside the project, click "Add a Service"
  2. Select "Application"
  3. Name: next-frontend
  4. Click "Create"

7.4 Configure the Application

Build Settings:

FieldValue
RepositoryYour GitHub repo
Branchmain
Build Path/
Build PackNext
Port3000

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/mydb

7.5 Deploy

  1. Click "Save"
  2. Click "Deploy"
  3. Monitor the build logs in real time
  4. 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 main

DokPloy 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 TypeHostValue
A@YOUR_ELASTIC_IP
AwwwYOUR_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:

  1. Go to Route 53 → Hosted Zones
  2. Select your domain
  3. Click "Create Record"
  4. Choose A record, enter your Elastic IP
  5. Save

  1. Add your domain to Cloudflare
  2. Update your registrar's nameservers to Cloudflare's
  3. Add an A record pointing to your Elastic IP
  4. Enable "Proxied" mode (orange cloud) for DDoS protection and CDN

8.4 Set Custom Domain in DokPloy

  1. Open your application in DokPloy
  2. Under "Domains", replace the generated URL with:
https://myapp.yourdomain.com
  1. 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

  1. Inside your project, click "Add a Service"
  2. Select "Database"
  3. Choose PostgreSQL
  4. Configure:
    • Name: postgres-db
    • Database: myapp
    • Username: dbuser
    • Password: Use a strong generated password
  5. 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/myapp

DokPloy services on the same project communicate using their service name as the hostname (e.g., postgres-db), not localhost.


9.3 Using AWS RDS Instead (Optional)

For production workloads, you may prefer Amazon RDS (managed PostgreSQL):

  1. Go to AWS Console → RDS → Create database
  2. Choose PostgreSQL, Free Tier (db.t3.micro)
  3. Place it in the same VPC as your EC2 instance
  4. Configure the Security Group to allow port 5432 from your EC2 instance's security group
  5. 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:

  1. Go to EC2 → Instances → Select instance → Monitoring tab
  2. View CPU utilization, network in/out, disk read/write

Set up a CloudWatch alarm for high CPU:

  1. Go to CloudWatch → Alarms → Create Alarm
  2. Select EC2 → Per-Instance Metrics → CPUUtilization
  3. Set threshold: > 80% for 5 consecutive minutes
  4. 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 traefik

Troubleshooting

Cannot Connect to DokPloy Dashboard (Port 3000)

Symptom: Browser times out at http://YOUR_IP:3000
  1. Verify the EC2 instance is Running in the console
  2. Check your Security Group — inbound rule for port 3000 must allow 0.0.0.0/0
  3. Confirm DokPloy container is running:
sudo docker ps | grep dokploy
  1. If not running, restart it:
sudo docker restart dokploy

SSH 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_IP

SSL 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.json has 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 to t3.medium

GitHub Webhook Not Triggering Auto-Deploy

Symptom: Pushing to GitHub doesn't trigger a new deployment
  1. Check Settings → Webhooks in your GitHub repository — the DokPloy webhook URL should be listed
  2. Click on the webhook and check "Recent Deliveries" for errors
  3. Ensure the server is reachable on port 3000 from the internet
  4. Try a manual deploy first to verify the configuration works
  5. 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/fstab

Option 2: Upgrade instance type (recommended)

  1. Stop the instance in AWS Console
  2. Go to Actions → Instance Settings → Change Instance Type
  3. Select t3.medium
  4. 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)

ComponentCost
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

OptionSavingsBest For
On-DemandBaselineFlexible / 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.medium brings the cost down to ~$18–20/mo — comparable to Contabo while staying on AWS infrastructure.

Comparison

PlatformMonthly 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:

  1. Create an S3 bucket in the AWS Console
  2. Create an IAM user with AmazonS3FullAccess policy
  3. Generate access keys for the IAM user
  4. 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-name

Use CloudFront as a CDN

  1. Go to CloudFront → Create Distribution
  2. Set your domain or EC2 Elastic IP as the origin
  3. Configure caching rules for static assets
  4. 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

  1. Go to AWS Backup → Create Backup Plan
  2. Select your EC2 instance and EBS volumes
  3. Schedule daily snapshots (e.g., keep last 7 days)
  4. Cost: ~$0.05/GB per snapshot per month

Security Hardening

Restrict SSH Access

Update the Security Group to only allow SSH from your IP:

  1. Go to EC2 → Security Groups → dokploy-sg
  2. Edit inbound rules for port 22
  3. Change source from 0.0.0.0/0 to "My IP"

Re-run this whenever your IP changes.


Install fail2ban

sudo apt install fail2ban -y
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

This 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 enable

Disable Root SSH Login

sudo nano /etc/ssh/sshd_config
# Set: PermitRootLogin no
# Set: PasswordAuthentication no
sudo systemctl restart sshd

Scaling Your Setup

Vertical Scaling (Resize Instance)

  1. Stop the instance
  2. Go to Actions → Instance Settings → Change Instance Type
  3. Choose a larger type (e.g., t3.large)
  4. 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:

  1. Create an Application Load Balancer (ALB) in EC2
  2. Launch 2+ EC2 instances with DokPloy
  3. Register instances in a Target Group
  4. Point your domain to the ALB DNS name
  5. 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 -y
ServiceURL
EC2 Instancesconsole.aws.amazon.com/ec2/v2/home#Instances
Elastic IPsconsole.aws.amazon.com/ec2/v2/home#Addresses
Security Groupsconsole.aws.amazon.com/ec2/v2/home#SecurityGroups
CloudWatchconsole.aws.amazon.com/cloudwatch
Billingconsole.aws.amazon.com/billing
Cost Explorerconsole.aws.amazon.com/cost-management/home

DokPloy Dashboard

PageURL
Dashboardhttp://YOUR_ELASTIC_IP:3000
After domain setuphttps://dokploy.yourdomain.com

Next Steps

After completing this guide, consider:

  1. Move DokPloy dashboard to a subdomain — set dokploy.yourdomain.com as the dashboard domain with SSL
  2. Set up automated EC2 snapshots — use AWS Backup for daily EBS snapshots
  3. Use RDS for databases — more reliable and easier to manage than self-hosted Postgres on EC2
  4. Add CloudFront — serve static assets faster and reduce EC2 bandwidth costs
  5. Configure a staging environment — deploy a staging branch to a separate subdomain automatically
  6. Set up CloudWatch alarms — get notified of high CPU, memory, or disk usage
  7. 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.small to c6i.4xlarge with 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.