A reference guide compiled from deploying two Node.js/Docker apps to AWS EC2, covering the real issues hit and how they were fixed.
1.
Getting Connected Q: How do I SSH into my EC2 instance?
Type when asked about the fingerprint the first time.
Q: doesn't seem to work / I get "bad permissions" / "Permission denied (publickey)" This happens when your key sits on a Windows drive mounted into WSL (e.g. ).
NTFS doesn't honor Linux permission bits properly.
Fix: copy the key into WSL's native filesystem first.
Q: My key filename has spaces in it — how do I reference it?
Wrap it in quotes: Q: How do I know which actual instance/IP I'm connected to?
Compare this to what the AWS Console shows for your instance — it's easy to accidentally SSH into an old instance if an Elastic IP got reassigned.
2.
Domain Name / HTTPS Without Buying a Domain Q: I don't want to buy a domain — can I still get real HTTPS?
Yes — use sslip.io.
Any hostname like automatically resolves to that IP with zero signup.
Let's Encrypt (via Certbot) will issue a real, trusted certificate for it just like a paid domain.
Q: Why can't I just use the raw IP with HTTP?
Clerk (auth) and Razorpay (payments) both require HTTPS with a real hostname in production/live mode.
Plain will not work with either.
Q: I later bought a real domain — how do I switch over?
In your registrar's DNS panel, add an A record: Host → your Elastic IP (add one for too if wanted).
Wait for propagation ( should return your IP).
Update your Nginx and re-run Certbot with the new domain.
Update Clerk/Razorpay webhook URLs and CORS settings to the new domain.
3.
Elastic IP Q: Do I need an Elastic IP?
Yes — without one, your instance's public IP changes on every stop/start, breaking your domain/cert setup.
Allocate one (EC2 → Elastic IPs → Allocate) and associate it with your instance (free while attached to a running instance).
4.
Disk Space Q: fails with "no space left on device" Default EC2 root volumes are often only ~8GB (or less after later resizes are lost) — too small for Docker image layers.
Check with: Quick relief: (removes unused images/cache, doesn't touch running containers' data).
Q: How do I permanently fix a too-small disk?
AWS Console → EC2 → Volumes → select the volume → Actions → Modify Volume → increase size (e.g. 25 GiB) → Modify.
No downtime.
On the server, extend the partition:
5.
Memory / Swap Q: My Docker build crashes with "JavaScript heap out of memory" or gets silently killed Free-tier EC2 instances (t2/t3.micro) typically have ~1GB RAM — not enough for / under load.
Fix: add swap space.
Q: fails with "No space left on device" while adding swap Your disk is full — fix disk space first (Section 4), then retry the swap commands.
Q: A container keeps restarting with a clean "Exited (0)" or crash-looping Check for OOM kills: If , add swap.
If not, check disk space and container logs for the real error.
Q: Node's build step still runs out of memory even with swap Explicitly raise Node's own heap limit in the Dockerfile:
6.
Nginx + Certbot (Reverse Proxy + Free SSL) Q: Basic Nginx + Certbot setup for a Docker app (server on :5000/5001, client on :5173)?
Then create : Q: Why get the cert with before starting Nginx, instead of ?
Because your Nginx config already references the cert files (chicken-and-egg problem) — would fail before the cert exists.
Getting it standalone first (with Nginx stopped, freeing port 80) avoids this.
Q: How do I swap a self-signed cert for a real Let's Encrypt one later?
Q: Can I use Caddy instead of Nginx+Certbot?
Yes — Caddy auto-issues and renews HTTPS certs with zero manual Certbot commands.
A minimal Caddyfile: Only run one of Nginx or Caddy at a time — both fight over ports 80/443.
7.
Docker Compose Gotchas Q: vs (hyphen)?
Newer Docker installs ( package) ship the Compose plugin, invoked as (space).
The old standalone binary may not exist.
If isn't found, use instead, or .
Q: running docker without sudo Q: file not found even though I created it Check