MEAN CRUD app (MongoDB, Express, Angular 15, Node.js) with Docker, Nginx reverse proxy, and CI/CD workflow for containerized deployment.
- Angular static build served by Nginx on port 80
- Nginx proxies
/apito the Node/Express backend - MongoDB runs as a container via Docker Compose
- Health checks on all services for zero-downtime updates
- Graceful shutdown handling prevents data loss during restarts
- MongoDB persistence via named Docker volume
cd backend
npm install
node server.jscd frontend
npm install
ng serve --port 8081Navigate to http://localhost:8081/.
The dev server proxies /api to http://localhost:8080 via frontend/proxy.conf.json.
Dockerfiles are in backend/Dockerfile and frontend/Dockerfile. Nginx config is in frontend/nginx.conf.
copy .env.example .envUpdate DOCKERHUB_USERNAME in .env.
docker compose build
docker compose up -dApp will be available at http://localhost/.
MongoDB data is stored in the mongo_data named volume, which persists across container restarts. To preserve data during upgrades, never use docker compose down (this removes volumes). Instead:
docker compose stop
docker compose up -dOr upgrade safely without stopping:
docker compose pull
docker compose up -d- Create an Ubuntu VM (t2.micro or similar) and open inbound port 80.
- Install Docker and Docker Compose plugin:
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo $VERSION_CODENAME) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker $USER- Create an app directory and copy files:
sudo mkdir -p /opt/mean-app
sudo chown $USER:$USER /opt/mean-appCopy these files into /opt/mean-app on the VM:
- Make deploy script executable:
chmod +x /opt/mean-app/deploy.sh- Run the stack:
cd /opt/mean-app
bash deploy.shThe application uses:
- Health checks on all services (MongoDB, backend, frontend) to ensure containers are fully ready before accepting traffic
- Graceful shutdown (30-second
stop_grace_period) to allow in-flight requests to complete - Service dependencies configured to only start containers when their dependencies are healthy
The deploy.sh script:
- Pulls latest images
- Updates containers with
docker compose up -d(no downtime) - Waits for services to pass health checks before declaring success
To manually deploy safely:
cd /opt/mean-app
bash deploy.shDO NOT use docker compose down (removes volumes and causes data loss). Always use:
docker compose pull
docker compose up -dWorkflow file: .github/workflows/ci-cd.yml
DOCKERHUB_USERNAMEDOCKERHUB_TOKENVM_HOSTVM_USERVM_SSH_KEY
On each push to main, the workflow:
- Builds and pushes updated images to Docker Hub
- SSHes into the VM and runs
deploy.shfor zero-downtime updates - Automatically cleans up dangling images
-
Docker Hub repositories/tags after push
- Link for Docker Hub repositories: https://hub.docker.com/u/ghbackend
-
GitHub Actions build/push logs
-
Deployment Link: http://188.166.214.195/
- Nginx reverse proxy config
nginx.conf: frontend/nginx.conf
- VM/container status
Nginx runs inside the frontend container and serves the Angular build. It proxies API requests under /api to the backend container. The reverse proxy configuration is in frontend/nginx.conf.
- Backend reads MongoDB URL from
MONGO_URIand defaults tomongodb://localhost:27017/dd_dbif not set. - Frontend uses a relative API base path, so it works behind the Nginx proxy.



