Skip to main content

Command Palette

Search for a command to run...

πŸš€ Deploying Strapi on AWS with Docker & Terraform: Real Problems, Real Fixes (A Practical DevOps Journey)

Published
β€’5 min read

Deploying a modern Node.js CMS like Strapi on AWS sounds simple on paper:

β€œJust Dockerize it, spin up an EC2 with Terraform, and run the container.”

In reality, I faced multiple real-world issues across:

  • Dockerfile & .dockerignore

  • Native Node.js dependencies

  • Image size vs EC2 limits

  • AWS EC2 memory constraints

  • Terraform user_data failures

  • Registry image tag issues

  • Debugging logs on Ubuntu

This blog documents every problem I hit, why it happened, and how I fixed it β€” so you don’t repeat the same mistakes.


🧱 High-Level Architecture & Flow

Overall Flow:

  1. Create Strapi project locally

  2. Dockerize Strapi (Dockerfile + .dockerignore)

  3. Build & push image to Docker Hub / ECR

  4. Provision EC2 using Terraform

  5. Configure system (Docker, swap, disk) via user_data

  6. Pull & run container on EC2

  7. Debug using Ubuntu logs

Local Dev β†’ Docker Image β†’ Registry (Docker Hub/ECR)
                           ↓
                      Terraform (EC2)
                           ↓
                    user_data (Docker + Swap)
                           ↓
                      Run Strapi Container

🐳 Problem 1: Node Native Module Error (better-sqlite3)

❌ Error

Error: The module better_sqlite3.node was compiled against a different Node.js version

πŸ” Root Cause

I installed node_modules locally and copied them into Docker.
Native modules like better-sqlite3 are compiled against:

  • Your OS

  • Your Node version

Docker runs a different OS + Node version, so binaries break.

βœ… Fix

Never copy node_modules into Docker.
Install dependencies inside the container.

FROM node:20-bullseye
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "run", "develop"]

Add .dockerignore:

node_modules
.git
.cache
dist
.env

🧠 Lesson

Native dependencies are OS and runtime specific. Always compile them inside the container environment.


🐧 Problem 2: Alpine vs Debian (glibc vs musl)

❌ Error

Error loading shared library ld-linux-x86-64.so.2

πŸ” Root Cause

I built the image on Debian (bullseye) and ran it on Alpine.
Native binaries compiled for glibc don’t run on Alpine’s musl libc.

βœ… Fix

Use the same base image for build and runtime:

FROM node:20-bullseye

🧠 Lesson

Native binaries are not portable across Linux libc implementations.


πŸ“¦ Problem 3: .dockerignore Not Working

❌ Mistake

I named the file:

.Dockerignore   ❌

Docker only recognizes:

.dockerignore   βœ…

πŸ” Root Cause

Docker filenames are case-sensitive. My ignore file was silently ignored, so node_modules were copied into the image.

βœ… Fix

mv .Dockerignore .dockerignore
docker build --no-cache -t my-image .

🧠 Lesson

Tiny naming mistakes can cause massive runtime failures.


πŸ” Problem 4: Missing Strapi Secrets in Docker

❌ Error

Missing admin.auth.secret configuration

πŸ” Root Cause

Strapi requires secrets from .env.
In Docker, .env was not passed to the container.

βœ… Fix

Create .env:

HOST=0.0.0.0
PORT=1337
APP_KEYS=key1,key2
API_TOKEN_SALT=salt
ADMIN_JWT_SECRET=secret
JWT_SECRET=secret

Run container:

docker run -p 1337:1337 --env-file .env my-image

🧠 Lesson

Apps that work locally often fail in containers because environment variables are missing.


🧠 Problem 5: EC2 t3.micro Memory Limits

❌ Issue

Strapi + Docker on t3.micro (1GB RAM) caused:

  • Slow startup

  • OOM kills

  • Image pull failures

πŸ” Root Cause

Low RAM + heavy image (~1.7GB) + Node runtime = memory pressure.

βœ… Fix: Add Swap via Terraform user_data

user_data = <<-EOF
#!/bin/bash
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
apt-get update -y
apt-get install -y docker.io
systemctl start docker
systemctl enable docker
EOF

🧠 Lesson

Low-cost instances need system tuning (swap, disk) for container workloads.


πŸ’Ύ Problem 6: EC2 Disk Space Too Small

❌ Issue

Default root volume (~8GB) filled quickly:

  • OS

  • Docker layers

  • Large image layers

βœ… Fix: Increase Root Volume in Terraform

root_block_device {
  volume_size = 20
  volume_type = "gp3"
}

🧠 Lesson

Disk space matters for Docker even more than RAM in image-heavy workloads.


🧩 Problem 7: Image Tag Not Found (manifest unknown)

❌ Error

manifest for lavkushwaha01/strapi-app:v4 not found

πŸ” Root Cause

Image existed locally, but I forgot to push that tag to Docker Hub.

βœ… Fix

docker push lavkushwaha01/strapi-app:v4

Then on EC2:

docker pull lavkushwaha01/strapi-app:v4

🧠 Lesson

If EC2 can’t pull your image, it doesn’t exist in the registry.


πŸ› οΈ Debugging Ubuntu & EC2: Important Commands

πŸ” System Logs

sudo journalctl -xe
sudo journalctl -u docker

πŸ” Cloud-init (Terraform user_data logs)

sudo cat /var/log/cloud-init-output.log
cloud-init status

πŸ” Docker Logs

docker ps
docker logs -f <container_id>
docker system df
docker images

πŸ” Memory & Disk

free -h
df -h
htop

🧠 How Logs Work

  • cloud-init: runs Terraform user_data on first boot

  • journalctl: systemd service logs

  • docker logs: app container logs

  • df/free: system resource usage


🧠 How I’ll Think Differently Next Time

❌ Old Approach

β€œLet me just Dockerize and run it on EC2.”

βœ… New DevOps Mindset

Before deploying, I now ask:

  • 🧠 What base image fits my native dependencies?

  • 🧱 How big is my image vs instance disk?

  • πŸ’Ύ Does the instance have enough RAM?

  • πŸ” Are secrets configured for containers?

  • πŸ§ͺ Can my image be pulled from the registry?

  • πŸ“œ Where will I debug when it fails?


🎯 Final Takeaway

Real DevOps is not about writing perfect Terraform or Dockerfiles on the first try.
It’s about:

Understanding systems deeply enough to debug failures calmly.

Every error I faced taught me:

  • How Linux works

  • How Docker images actually run

  • How cloud infra behaves under resource pressure


🏁 Conclusion

This deployment journey taught me more than any tutorial ever could.
If you’re learning Docker, Terraform, AWS, or Strapi β€” break things intentionally, then fix them. That’s how you become production-ready.