DevOps & Infrastructure

Deploy Node.js App: GitHub Actions + Docker CI/CD

Your Node.js app crashing because you forgot to restart the server? This CI/CD pipeline with GitHub Actions and Docker fixes that—automatically. No more manual babysitting.

CI/CD pipeline diagram: GitHub push triggers Docker build and deploy to EC2 instance

Key Takeaways

  • Automates Node.js deploys via GitHub Actions SSH to EC2—push and forget.
  • Docker Compose simplifies local/prod parity; prune images to avoid bloat.
  • Cheap for prototypes, but watch AWS bills and security holes for scale.

Imagine this: you’re tweaking your Node.js side project at midnight, push to GitHub, and boom—it’s live on the web without you touching a server. That’s the promise of deploying a Node.js & Express application using CI/CD with GitHub Actions and Docker. For real people—solo devs, indie hackers—it means ditching the panic deploys that kill your weekend.

But here’s the thing. We’ve heard this song before. Back in 2010, everyone swore by Capistrano for Ruby deploys. Flash forward, and it’s all vendor lock-in and surprise bills. Who’s really winning? GitHub (Microsoft) gets your code metadata, AWS charges for that t2.micro instance ticking away, Docker Inc. pushes their hub. You? Maybe a smoother workflow—if it doesn’t break first.

Look, this tutorial nails the basics. Push code, trigger Actions, SSH to EC2, rebuild Docker, spin up Compose. Clean. But let’s peel back the PR shine.

Why Does This Matter for Busy Developers?

You’re not a full-time DevOps drone. (Lucky you.) This setup lets a simple git push handle the grunt work: pulling code, pruning images, rebuilding containers. No more “it works on my machine” excuses.

And—crucial for mortals—Docker Compose keeps it declarative. One YAML file, docker compose up -d, done. Test locally first with curl http://localhost:8080 spitting back “Hello from the server!” Then it mirrors production on EC2.

But cynicism check: EC2 public IPs? Fine for prototypes. Production? Begging for DDoS or port scans. Security groups are your flimsy shield—open 8080 and pray.

Straight from the Workflow: The Magic Script

name: Deploy Node App on: push: branches: - main jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v4 - name: Deploy via SSH uses: appleboy/[email protected] with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USERNAME }} key: ${{ secrets.SSH_KEY }} script: | set -e cd /home/ubuntu/node-app/Node.js-App-Deploy-Github-Action echo “Pulling latest code…” git pull origin main echo “Stopping existing containers…” docker compose down echo “Removing unused images…” docker image prune -f echo “Building and starting containers…” docker compose up -d –build echo “Deployment completed successfully”

That’s the beating heart. set -e kills it on failure—no half-baked deploys. docker image prune -f fights disk bloat. Smart touches, straight from Omkar Sharma’s repo.

I love the SSH action. Generate keys locally (ssh-keygen -t rsa -b 4096), slap the pubkey in EC2’s authorized_keys, secret the private one in GitHub. Boom, passwordless trust.

Is GitHub Actions + Docker Actually Secure?

Short answer: ish. SSH keys are better than passwords, but GitHub Secrets? They’re encrypted, sure. Still, breaches happen—SolarWinds, anyone? And that EC2 box? sudo apt update && sudo apt upgrade -y is step one, but you’re one bad apt install from owning a botnet zombie.

Unique angle nobody mentions: this is 2024’s FTP. Remember uploading zips via FileZilla? Manual hell. Capistrano over SSH? Marginally better. Now Actions abstracts it, but you’re still SSH-ing code to a VPS. Prediction: in two years, Vercel or Fly.io eats this lunch with git-push-to-deploy, no EC2 tax.

Dockerfile’s lean: FROM node:22-alpine, copy package.json first for layer caching, EXPOSE 8080, CMD ["node", "index.js"]. Express app couldn’t be simpler—app.get('/', (req, res) => res.send('Hello from the server!')). Runs on PORT 8080, env-aware.

Test cycle? docker build -t node-app . then docker run -d -p 8080:8080 --rm node-app. Curl it. Down with docker compose down. Perfect local loop before EC2 roulette.

Hidden Gotchas That’ll Bite Newbies

EC2 setup: Launch Ubuntu, open 8080 in security group, SSH in, install Docker + Compose via apt. Clone repo, docker compose up -d. Hit public IP:8080. Easy.

But. SSH from Actions needs username (ubuntu), host (public IP), key secret. Repo path hardcoded—change it, pipeline dies. No health checks? Container up, app crashed? You’re blind.

Costs sneak up. T2.micro free tier? Sure. Scale to traffic, and AWS laughs to the bank. Docker overhead on tiny instances? Latency spikes.

And GitHub minutes: free for public repos, but private? Burn through ‘em fast on rebuilds. Who’s paying? You, via upgrade.

Still, for a $5/month hobby app, unbeatable.

So, roll your own. Fork Omkar’s repo (omkarsharma2821), tweak index.js, push. Watch Actions light up. Live in seconds.

Historical parallel: this echoes Heroku’s 2007 glory—push to deploy, no servers. They got fat, pricey. Open source Actions? Free(ish), but fragmented.

Will This Scale Beyond Hello World?

Nope, not out the box. No load balancing, no secrets management beyond env, no monitoring. Add Prometheus? Sure. But that’s another pipeline.

For real apps: swap SSH for AWS CodeDeploy or ECS. Or ditch EC2 for Lightsail, cheaper.

Bottom line. It works. Cuts deploy time from hours to minutes. Skeptical vet says: use it to ship faster, not to solve world hunger.

**


🧬 Related Insights

Frequently Asked Questions**

How do I set up SSH keys for GitHub Actions to EC2?

Generate with ssh-keygen -t rsa -b 4096. Copy pubkey to EC2’s ~/.ssh/authorized_keys. Paste private key (sans headers) into GitHub repo Secrets as SSH_KEY. Add SSH_HOST (EC2 IP) and SSH_USERNAME (ubuntu).

What’s the monthly cost for this Node.js CI/CD on AWS?

T2.micro: free tier eligible (~$0 first year). Data transfer: pennies. Watch EBS volume growth from Docker images—prune helps.

Can I use this pipeline with Render or DigitalOcean instead of EC2?

Yes. Swap SSH host/script for their APIs. DigitalOcean Droplets mirror EC2 setup perfectly.

Aisha Patel
Written by

Former ML engineer turned writer. Covers computer vision and robotics with a practitioner perspective.

Frequently asked questions

How do I set up SSH keys for GitHub Actions to EC2?
Generate with `ssh-keygen -t rsa -b 4096`. Copy pubkey to EC2's `~/.ssh/authorized_keys`. Paste private key (sans headers) into GitHub repo Secrets as SSH_KEY. Add SSH_HOST (EC2 IP) and SSH_USERNAME (ubuntu).
What's the monthly cost for this Node.js CI/CD on AWS?
T2.micro: free tier eligible (~$0 first year). Data transfer: pennies. Watch EBS volume growth from Docker images—prune helps.
Can I use this pipeline with Render or DigitalOcean instead of EC2?
Yes. Swap SSH host/script for their APIs. DigitalOcean Droplets mirror EC2 setup perfectly.

Worth sharing?

Get the best Open Source stories of the week in your inbox — no noise, no spam.

Originally reported by Dev.to

Stay in the loop

The week's most important stories from Open Source Beat, delivered once a week.