Case Study
End-to-End Deployment of Guidra Backend on Azure VM
Azure VM → Nginx → Node.js → MongoDB Atlas
Executive Summary
This case study documents the production deployment of the Guidra backend API on Microsoft Azure infrastructure. The implementation demonstrates production-oriented deployment practices including security hardening, reverse proxy configuration, SSL termination, process management, and cloud database integration.
Technologies Used:Azure VM (Ubuntu 24.04), Nginx, Node.js, PM2, MongoDB Atlas, Let's Encrypt
Key Metrics:
- API latency: ~150–230ms
- Memory usage: ~600MB (idle)
- Uptime tested: 7+ days
- SSL rating: A (Qualys SSL Labs)
- Deployment time: less than 30 seconds (automated script)
- Availability: verified during deployment and validation testing
1. System Architecture
Internet → Azure NSG → UFW → Nginx (reverse proxy) → PM2 → Node.js App → MongoDB Atlas
| Layer | Technology | Purpose |
|---|---|---|
| Cloud | Azure VM (B2als v2) | Compute infrastructure |
| Network | Azure NSG + UFW | Defense-in-depth firewall |
| Web Server | Nginx | Reverse proxy, SSL termination |
| Runtime | Node.js 20 LTS | Application execution |
| Process | PM2 | Process management, auto-restart |
| Database | MongoDB Atlas | Cloud database service |
| Security | fail2ban, SSH hardening | Intrusion prevention |
| SSL | Let's Encrypt | Free automated certificates |
2. Deployment Phases
Infrastructure Foundation
- Azure VM provisioning with static public IP
- SSH key authentication (password-less)
- Deploy user creation (non-root)
- UFW configuration (ports 22, 80, 443 only)
Security Implementation
- SSH hardening (/etc/ssh/sshd_config modifications)
- fail2ban configuration with custom jail
- Automatic security updates enabled
- Azure NSG rules configured
Application Stack
- NVM + Node.js LTS installation
- PM2 process manager setup
- MongoDB Atlas connection with SRV string
- Environment variable management
Web Server Configuration
- Nginx reverse proxy setup
- SSL certificates via Let's Encrypt
- HTTP → HTTPS redirection
- Custom domain configuration
Deployment Automation
- Git-based deployment script
- SSH config for GitHub authentication
3. Challenges & Solutions
| Challenge | Solution | Engineering Impact |
|---|---|---|
| SSH lockout during fail2ban testing | Azure portal emergency SSH key reset | Understood cloud recovery paths |
| Nginx only listening on IPv6 | Added listen 0.0.0.0:80; directive | Learned socket binding nuances |
| Domain DNS propagation delay | Used dnschecker.org for verification | Real-world DNS debugging |
| Azure NSG blocking port 80 | Added inbound rule in portal | Cloud firewall configuration |
| GitHub SSH key confusion | Created ~/.ssh/config with explicit IdentityFile | SSH client mastery |
| fail2ban config overwrite risk | Used jail.local instead of modifying jail.conf | Configuration best practices |
Key Incident: Self-inflicted SSH ban led to Azure emergency recovery-validated both fail2ban functionality and cloud provider fallback mechanisms.
4. Security Hardening
Defense in Depth Implementation
| Layer | Technology | Configuration |
|---|---|---|
| Network Edge | Azure NSG | Inbound rules: 22,80,443 only |
| Host Firewall | UFW | Default deny, explicit allow |
| Intrusion Prevention | fail2ban | 3 strikes → 1 hour ban |
| Access Control | SSH | Keys only, root disabled |
| Updates | unattended-upgrades | Automatic security patches |
| Encryption | Let's Encrypt | 90-day certs, auto-renewal |
SSH Hardening Parameters
PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes MaxAuthTries 3 AllowUsers deploy
fail2ban Configuration
[sshd] enabled = true maxretry = 3 bantime = 3600 findtime = 600
5. Production Validation
Tests Performed
| Test | Method | Result |
|---|---|---|
| Server reboot persistence | sudo reboot followed by SSH | ✅ PM2 auto-restart verified |
| Process crash recovery | kill -9 <node-pid> | ✅ PM2 respawned within 2s |
| Nginx auto-start | Server reboot check | ✅ Nginx active post-reboot |
| SSL auto-renewal | certbot renew --dry-run | ✅ Successful |
| Firewall rules | nmap -p 22,80,443 from external | ✅ Only specified ports open |
| DNS resolution | dig guidra.tech | ✅ Correct A record |
| Reverse proxy | Direct port 3000 vs 443 | ✅ Headers preserved |
| Health endpoint | Continuous monitoring | ✅ 200 OK response |
Logging Infrastructure
/var/www/guidra/logs/ # Application logs (out/err/combined) /var/log/nginx/ # Web server access/error logs /var/log/fail2ban.log # Intrusion attempts /var/log/auth.log # Authentication events
6. Deployment Workflow
Simple, Reliable Deployment
# Local development git add . git commit -m "feature update" git push # Server deployment (single command) ssh deploy@guidra.tech './deploy-guidra.sh'
Deploy Script Logic
cd /var/www/guidra/current git pull npm ci --only=production || npm install --production pm2 restart guidra-api --update-env
CI/CD Philosophy: Manual trigger with automation-balance of control and efficiency for solo development.
Deployment is triggered manually from the local machine using SSH and a deployment script.
7. Key Lessons & Engineering Insights
Technical Mastery Gained
- Network layering: NSG vs UFW vs application-level filtering
- DNS mechanics: Propagation, TTL, record types
- Socket binding: localhost vs 0.0.0.0 implications
- Process management: PM2's role in production reliability
- Configuration management: Never edit original config files
Operational Wisdom
- Always test security features with backup access
- Document recovery procedures before needing them
- Monitor logs before monitoring metrics
- Simple automation beats complex tooling
"The day I banned myself from my own server taught me more about fail2ban, Azure recovery, and SSH than any tutorial could."
8. Future Improvements
| Priority | Improvement | Rationale |
|---|---|---|
| High | Automated backups to S3 | Data durability |
| High | Rate limiting implementation | API abuse prevention |
| Medium | PM2 monitoring dashboard | Visual metrics |
| Medium | GitHub Actions CI/CD | Fully automated deploys |
| Low | Docker containerization | Environment consistency |
| Low | Multi-instance scaling | Horizontal scalability |
Hosted Domains
These endpoints were previously hosted as part of the deployment. They are shown here as deployment references rather than active live services.
Frontend: previously hosted at https://guidra.tech
Backend API: previously hosted at https://api.guidra.tech
Health Endpoint: previously hosted at https://api.guidra.tech/health
9. Conclusion
This deployment transformed a fresh Azure VM into a production-tested backend environment that hosted the Guidra API on custom domains during the deployment period. The implementation encompasses security hardening, reverse proxy configuration, SSL termination, process management, and cloud database integration-all providing a reliable and maintainable production setup.
The experience provided invaluable hands-on learning in system administration, networking, and DevOps that textbooks cannot replicate. From recovering from self-inflicted SSH bans to debugging Nginx listen directives, each challenge reinforced practical engineering skills.
The experience provided hands-on learning in system administration, networking, and DevOps beyond textbook environments.
Engineering Competencies Demonstrated
- Linux System Administration
- Cloud Infrastructure (Azure)
- Network Security (NSG, UFW, fail2ban)
- Web Server Configuration (Nginx)
- Process Management (PM2)
- Database Integration (MongoDB Atlas)
- SSL/TLS Implementation
- DNS Configuration
- Deployment Automation
- Incident Recovery