π Deploying Strapi on AWS with Docker & Terraform: Real Problems, Real Fixes (A Practical DevOps Journey)
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 &
.dockerignoreNative Node.js dependencies
Image size vs EC2 limits
AWS EC2 memory constraints
Terraform
user_datafailuresRegistry 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:
Create Strapi project locally
Dockerize Strapi (Dockerfile + .dockerignore)
Build & push image to Docker Hub / ECR
Provision EC2 using Terraform
Configure system (Docker, swap, disk) via
user_dataPull & run container on EC2
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 Terraformuser_dataon first bootjournalctl: systemd service logsdocker logs: app container logsdf/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.