In the current software development landscape, containerization has become a fundamental pillar for ensuring consistent environments, rapid deployments, and horizontal scalability. In this article, the second part of a series dedicated to building infrastructure on AWS, we explore how to apply Docker and Docker Compose to prepare a note-taking application (note-taker) before migrating it to the cloud. This approach not only facilitates the transition to cloud services like AWS ECS or EKS, but also lays the foundation for a robust, maintainable architecture ready to integrate artificial intelligence, cybersecurity, and automation capabilities.
The decision to containerize first, before touching cloud infrastructure, responds to a practical need: ensuring the application behaves identically locally, in a staging environment, and in production. Many teams make the mistake of jumping straight into building VPCs, subnets, and load balancers, only to discover that the code does not behave the same inside the container as on the developer's machine. At Q2BSTUDIO, we understand that early containerization drastically reduces “it works on my machine” problems and accelerates the delivery cycle.
Our project is a web application with a Node.js backend and a React frontend, plus a PostgreSQL database. For each component, we wrote specific Dockerfiles optimized for production. For the backend, we chose node:22-slim instead of Alpine versions because we needed to compile native dependencies like sqlite3 (though we later migrated to PostgreSQL). The slim image provides the necessary libraries without the extra weight of a full image. We used npm ci --omit=dev to install only production dependencies, reducing the final container size and minimizing the attack surface — a key aspect of cybersecurity in cloud environments.
For the frontend, we opted for a multi-stage build. The first stage builds the React application with Node.js Alpine; the second serves the static files using Nginx. Why Nginx? Because it is much lighter and faster than keeping a Node server running just to deliver HTML and JS. Additionally, Nginx acts as a reverse proxy to forward /api and /health requests to the backend, solving a common issue in React applications: the frontend does not know how to reach the backend if they are in separate containers. Our Nginx configuration, with proxy_pass and try_files directives, allows React to handle client-side routing without 404 errors when the page is reloaded.
Docker Compose orchestrates the three services: backend, frontend, and database. We incorporated health checks so that the backend waits for PostgreSQL to be ready using pg_isready. We carefully mapped ports to avoid conflicts with local services like XAMPP or Docker Desktop. All services share a custom network called app-network, enabling communication by service name. This practice is identical to how networks are configured in AWS ECS or a Kubernetes cluster.
During development, we faced a typical issue: the backend forced SSL in production mode, but the local database did not support SSL connections. With NODE_ENV=production in docker-compose, the application rejected the connection. The solution was to refine the SSL logic to accept an explicit environment variable (PGSSL). This kind of incident underscores the importance of not assuming production behavior in test environments. At Q2BSTUDIO, we always recommend parameterizing security configuration and clearly separating environments through variables.
Once the application is fully containerized and running locally, the next step is to migrate the infrastructure to AWS using Terraform. Prior containerization allows us to deploy on any container service: ECS with Fargate, EKS with Kubernetes, or even on EC2 instances with Docker. It also facilitates integration with managed services like RDS for the database, or ElastiCache for caching. The same Docker image we tested locally is deployed to production unchanged, ensuring consistency.
For companies looking for custom software applications, this workflow is ideal. Containerization not only accelerates development, but also makes it easy to incorporate artificial intelligence (such as AI agents that process notes) or analytics systems with Power BI that query the database. Cybersecurity benefits from lighter images and the ability to scan vulnerabilities in each container layer. Likewise, process automation through CI/CD pipelines (GitHub Actions, GitLab CI) becomes trivial when the build is already packaged in Docker.
At Q2BSTUDIO, we apply this methodology in all our cloud projects. We combine containerization with cloud services AWS/Azure to deliver scalable, secure, and maintainable solutions. Infrastructure as code with Terraform and orchestration with Kubernetes are the natural complement. If your organization is evaluating migrating to the cloud or modernizing its applications, starting with containerization is the smart first step.
In summary, containerizing before cloudifying is not a detour, it's a shortcut. It saves you headaches, reduces debugging costs, and accelerates time-to-market. In future installments we will explore IAM configuration, VPC, and continuous deployment on AWS. Stay tuned.




