Smart Expense Splitter using Docker
Introduction
The core objective of this documentation is to provide a thorough, technically sound, and easily digestible overview of the system's architecture, deployment procedures, and the rationale behind the design choices, presented in a professional blog-friendly format. This project demonstrates proficiency in container orchestration, microservices design, and the deployment of a scalable, three-tier application using Docker and Docker Compose.
Objectives of Part 1 (DA1)
The initial phase, DA1, focused on establishing the foundational component of our application: persistent data storage. This stage was critical for validating the basic principles of containerization and volume management.
Component | Description |
Primary Objective | Containerization of a Core Service: Successfully deploy a single-container database instance to serve as the persistent storage layer for the application. |
Secondary Objective | Volume Persistence: Ensure data integrity across container restarts by correctly configuring Docker volumes. |
Container Involved | postgres:15-alpine |
Software Involved | Docker Engine (Containerization), PostgreSQL (Database Service) |
Objectives of Part 2 (DA2)
DA2 represented a significant architectural leap, transforming the project into a fully functional, multi-container microservice application. The focus shifted to service discovery, inter-container communication, and the implementation of a complete three-tier architecture with caching and a reverse proxy.
Component | Description |
Primary Objective | Microservice Orchestration: Deploy and manage a four-container application using Docker Compose. |
Secondary Objective | Performance and Security Layering: Introduce Nginx as a reverse proxy and Redis for in-memory caching to enhance performance and security. |
Containers Involved | postgres, web (Flask), nginx, redis |
Software Involved | Docker Compose (Orchestration), Flask (Web Application), Nginx (Reverse Proxy), Redis (In-Memory Cache) |
Objectives of Part 3 (DA3)
The final phase, DA3, is the documentation and formalization stage. It encompasses the analysis of the preceding development phases, the creation of comprehensive architectural diagrams, and the preparation of the project for public sharing via DockerHub.
Component | Description |
Primary Objective | Technical Documentation: Produce a detailed, high-quality technical report and blog post covering the entire project lifecycle. |
Secondary Objective | Reproducibility and Sharing: Prepare the final container images and source code for public distribution via DockerHub and GitHub. |
Containers Involved | N/A (Focus on documentation and deployment preparation) |
Software Involved | Markdown (Documentation), Mermaid/Diagramming Tools (Visualization), Git/GitHub, Docker/DockerHub (Distribution) |
Container and Software Inventory
The following table details the core components utilized across DA1 and DA2.
Name of Container/Software | Purpose/Role | Download/Source Link |
|---|
PostgreSQL (Container) | Persistent Data Layer: Stores all user, expense, and settlement data. | docker pull postgres:15-alpine |
Flask Application (Container) | Application Logic Layer: Handles API requests, business logic, and interaction with the database and cache. | Custom build from Dockerfile |
Nginx (Container) | Reverse Proxy/Load Balancer: Routes incoming traffic, serves static files, and provides a layer of security and performance. | docker pull nginx:alpine |
Redis (Container) | Caching/Session Management: Provides fast, in-memory storage for frequently accessed data and user sessions. | docker pull redis:7-alpine |
Docker Engine (Software) | Core platform for building, shipping, and running containerized applications. | Docker Official Website |
Docker Compose (Software) | Tool for defining and running multi-container Docker applications. | Docker Compose Documentation |
Python/Flask (Software) | Framework used to develop the core web application logic. | Flask Documentation |
Architectural Diagram

The final architecture of the Expense Splitter Platform is a classic three-tier microservice design orchestrated by Docker Compose. The first tier, the Presentation Layer, is handled by the Nginx Reverse Proxy. Nginx acts as the public-facing gateway, routing external HTTP requests to the appropriate backend service and potentially serving static assets directly. This layer is crucial for security, rate limiting, and ensuring the backend application remains isolated from the public internet.
The second tier, the Application Layer, is the custom-built Flask Web Application. This container houses the core business logic, including user authentication, expense calculation, and group settlement algorithms. It communicates internally with the other services over a dedicated Docker network. For data operations, it connects to the persistent PostgreSQL Database (the Data Layer), and for performance optimization, it utilizes the Redis Cache for fast retrieval of session data and frequently accessed information. This separation of concerns across four distinct containers ensures modularity, scalability, and ease of maintenance, embodying modern cloud-native development principles.
Procedure for Part 1
DA1 involved the initial setup of the PostgreSQL database container.
Step | Description |
|
1. Define the Service | Create a basic docker-compose.yml file defining the postgres service, specifying the image (postgres:15-alpine) and necessary environment variables (POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD). |
|
2. Configure Volume | Define a named volume (postgres_data) to ensure the database files persist outside the container lifecycle. |
|
3. Deploy and Verify | Execute docker compose up -d to start the container. Verify the container is running and healthy using docker ps and check the logs with docker compose logs postgres. |
|
Procedure for Part 2
DA2 involved expanding the architecture to include the Flask application, Nginx proxy, and Redis cache.
Step | Description |
|
1. Integrate Application | Add the web service to docker-compose.yml, specifying the build context and linking it to the postgres service using depends_on and the internal network URL. |
|
2. Add Caching and Proxy | Introduce the redis and nginx services. Configure Nginx to proxy requests to the web service's internal port (5000) and expose port 80 externally. |
|
3. Build and Run | Execute docker compose build to build the custom Flask image, followed by docker compose up -d to deploy the entire stack. |
|
4. Functional Testing | Access the application via the exposed Nginx port (e.g., http://localhost). Test core features like user registration and expense logging to ensure all services are communicating correctly. |
|
Procedure for Part 3
DA3 focuses on the final documentation and preparation for sharing.
Step | Description |
|
1. Documentation Draft | Structure the technical documentation (this document) following the required blog post format, including all objectives, component lists, and architectural descriptions. |
|
2. Diagram Generation | Use an online tool (like Mermaid Live Editor or a dedicated diagramming tool) to generate the final architectural diagram based on the four-container design. |
|
3. Final Review and Export | Conduct a thorough review of the document for technical accuracy and adherence to the required structure. Export the final document as a shareable file (e.g., PDF or a final Markdown file). |
|
Modifications Done to the Containers After Downloading
The core containers used (postgres, nginx, redis) were based on official, stable images. However, the application container (web) required specific modifications and hardening steps to be production-ready.
Step | Modification/Rationale |
1. Custom Dockerfile Creation | A multi-stage Dockerfile was created to build the Flask application. The first stage installs dependencies and builds the application, and the second, smaller stage copies only the necessary runtime files, significantly reducing the final image size and attack surface. |
2. Non-Root User Execution | The Dockerfile was modified to switch from the default root user to a dedicated, non-privileged user (e.g., appuser) for running the Flask application. This is a critical security best practice to mitigate potential container escape exploits. |
3. Healthcheck Implementation | Custom HEALTHCHECK instructions were added to the docker-compose.yml for all services. For the web container, a dedicated health endpoint (/api/health) was implemented in the Flask app and checked using curl. This ensures dependent services (like Nginx) only start once the application is fully ready. |
4. Environment Variable Parameterization | All sensitive credentials (database passwords, secret keys) and configuration parameters (ports, database names) were parameterized using environment variables (${POSTGRES_PASSWORD}, ${SECRET_KEY}). This allows for secure configuration management without hardcoding values into the image or docker-compose.yml.
|
Link:
https://hub.docker.com/r/megh5na/expense-splitter-enhanced2
Outcomes of the DA
The successful completion of this project yielded several key outcomes:
•Functional Microservice Architecture: A fully operational, containerized expense splitting application capable of handling user data, expense logging, and group settlements.
•Enhanced Scalability and Resilience: The use of Nginx and Redis introduced layers for traffic management and performance caching, making the application more resilient and scalable than a monolithic design.
•Mastery of Orchestration: Demonstrated practical expertise in using Docker Compose to define, link, and manage complex multi-container applications, including network and volume configuration.
•Professional Documentation: Produced comprehensive technical documentation (DA3) that clearly articulates the architectural design and implementation process, a crucial skill in professional software development.
Conclusion
This project successfully navigated the complexities of containerized application development, evolving from a simple database proof-of-concept to a production-ready, four-service microservice stack. The architectural decisions—specifically the implementation of Nginx, Flask, PostgreSQL, and Redis—were instrumental in creating a system that is not only functional but also adheres to industry best practices for security, performance, and maintainability. The documentation serves as a testament to the robust design and rigorous implementation process.
References and Acknowledgements
Original GitHub/DockerHub Sources
•IITB Docker Tutorial: This tutorial provided the foundational knowledge for understanding Docker networking and volume management.
•https://docker-tutorial.iitb.ac.in/
Comments
Post a Comment