A minimal note-taking web application built with Flask and MySQL, containerized using Docker and orchestrated with Docker Compose. This project provides both a simple web UI and a REST API for creating and listing notes, with persistent MySQL storage.
π GitHub Repository: https://github.com/davidahdy/flask-notes-compose
- Flask-based web app with HTML UI and REST API endpoints
- MySQL database with persistent storage using Docker volumes
- Environment variables for secure configuration management
- Docker Compose for multi-container orchestration
- Health checks for both web and database services
- Non-root container security implementation
- AWS EC2 deployment ready
Technology | Purpose |
---|---|
Flask | Python microframework for web development |
MySQL 8.x | Relational database for data persistence |
Docker | Containerization platform |
Docker Compose | Multi-container orchestration |
Gunicorn | WSGI HTTP server for production |
Before running this project, ensure you have:
- Docker (version 20.10+) and Docker Compose (v2) installed
- Git for cloning the repository
- Basic terminal/command line knowledge
- AWS EC2 instance (for cloud deployment) or local machine
flask-notes-compose/
βββ .env # Environment variables (DO NOT COMMIT)
βββ .env.example # Template for environment variables
βββ .gitignore # Git ignore rules
βββ Dockerfile # Web app container build file
βββ README.md # This documentation
βββ app/ # Flask application source code
β βββ __init__.py # Flask app initialization
β βββ static/ # Static assets (CSS, JS, images)
β βββ templates/ # HTML templates
β βββ index.html # Main UI template
βββ db/ # Database configuration
β βββ init/ # SQL initialization scripts
β βββ 001_create_table.sql
βββ docker-compose.yml # Docker Compose configuration
βββ docs/ # Documentation files
βββ requirements.txt # Python dependencies
The application uses the following environment variables (defined in .env
):
Variable | Description | Example |
---|---|---|
MYSQL_ROOT_PASSWORD |
MySQL root user password | secure_root_pass123 |
MYSQL_DATABASE |
Database name | notesdb |
MYSQL_USER |
MySQL application user | notesuser |
MYSQL_PASSWORD |
MySQL application user password | user_pass123 |
HOST_PORT |
Host port for web application | 8080 |
DB_USER |
Database user for Flask app | notesuser |
DB_PASSWORD |
Database password for Flask app | user_pass123 |
DB_NAME |
Database name for Flask app | notesdb |
DB_HOST |
Database hostname (container name) | db |
DB_PORT |
Database port | 3306 |
git clone https://github.com/davidahdy/flask-notes-compose.git
cd flask-notes-compose
cp .env.example .env
# Edit .env with your preferred MySQL credentials and HOST_PORT
docker compose up -d --build
# Check container status
docker compose ps
# View logs
docker compose logs -f web
# Test health endpoint
curl -i http://localhost:${HOST_PORT:-8080}/healthz
Expected health check response:
{
"status": "healthy",
"database": "connected",
"timestamp": "2025-08-13T10:30:45Z"
}
Access the web UI at:
- Local: http://localhost:8080
- EC2: http://<EC2_PUBLIC_IP>:8080
Method | Endpoint | Description | Request Body |
---|---|---|---|
GET |
/notes |
List all notes | N/A |
POST |
/notes |
Create a new note | {"content":"Your note text"} |
GET |
/healthz |
Health check | N/A |
curl -X POST http://localhost:8080/notes \
-H "Content-Type: application/json" \
-d '{"content":"Buy groceries for the week"}'
Response (201 Created):
{
"id": 1,
"content": "Buy groceries for the week",
"created_at": "2025-08-13T10:30:45Z",
"message": "Note created successfully"
}
curl http://localhost:8080/notes
Response (200 OK):
{
"notes": [
{
"id": 1,
"content": "Buy groceries for the week",
"created_at": "2025-08-13T10:30:45Z"
},
{
"id": 2,
"content": "Schedule dentist appointment",
"created_at": "2025-08-13T09:15:30Z"
}
],
"total": 2
}
curl http://localhost:8080/healthz
Response (200 OK):
{
"status": "healthy",
"database": "connected",
"timestamp": "2025-08-13T10:30:45Z"
}
MySQL data is stored in a Docker named volume (db-data
) ensuring persistence between container restarts.
To completely wipe all data:
docker compose down -v # Remove containers and volumes
docker compose up -d # Restart with fresh database
# Create backup
docker compose exec db mysqldump -u root -p notesdb > backup.sql
# Restore backup
docker compose exec -T db mysql -u root -p notesdb < backup.sql
# All services
docker compose logs -f
# Specific service
docker compose logs -f web
docker compose logs -f db
# Stop containers (keep data)
docker compose down
# Stop containers and remove volumes (lose data)
docker compose down -v
docker compose restart
Issue | Cause | Solution |
---|---|---|
Access denied for user | Incorrect MySQL credentials | Verify .env credentials match MYSQL_USER /MYSQL_PASSWORD |
Port already in use | Another service using the port | Change HOST_PORT in .env to an available port |
Health check failing | Database not ready or connection issues | Check logs: docker compose logs db web |
Database schema missing | Initialization scripts not executed | Reset containers: docker compose down -v && docker compose up -d |
Container won't start | Resource constraints or Docker issues | Check Docker Desktop resources and restart Docker |
# Check container resource usage
docker stats
# Inspect container details
docker compose exec web ps aux
docker compose exec db ps aux
# Test database connectivity
docker compose exec db mysql -u root -p -e "SHOW DATABASES;"
# Check application logs inside container
docker compose exec web tail -f /var/log/flask-app.log
Ensure your EC2 Security Group allows:
- Inbound: Port 8080 (or your
HOST_PORT
) from0.0.0.0/0
- Outbound: All traffic
-
Launch EC2 Instance
- Use Amazon Linux 2 or Ubuntu 20.04+
- Install Docker and Docker Compose
- Configure Security Group for port 8080
-
Deploy Application
# On EC2 instance git clone https://github.com/davidahdy/flask-notes-compose.git cd flask-notes-compose cp .env.example .env # Edit .env with production credentials docker compose up -d --build
-
Access Application
- Web UI:
http://<EC2_PUBLIC_IP>:8080
- API:
http://<EC2_PUBLIC_IP>:8080/notes
- Web UI:
- Use strong, unique passwords in production
.env
- Set up SSL/TLS termination with a reverse proxy (nginx)
- Implement log rotation and monitoring
- Regular database backups
- Consider using AWS RDS for database in production
- Never commit
.env
files to version control - Use strong passwords for all database credentials
- Restrict database access to trusted networks only
- Run containers as non-root user (implemented in Dockerfile)
- Keep Docker images updated regularly
- Monitor application logs for suspicious activity
- Web UI loads successfully
- Can create notes via web form
- Notes persist after container restart
- API endpoints return correct responses
- Health check reports healthy status
- Database connection works properly
# Run basic API tests
./scripts/test-api.sh # (if you create this script)
βββββββββββββββββββ ββββββββββββββββββββ
β Web Browser β β API Client β
β (Port 8080) β β (curl/Postman) β
βββββββββββ¬ββββββββ βββββββββββ¬βββββββββ
β β
ββββββββββββ¬ββββββββββββ
β
βΌ
βββββββββββββββββββ
β Flask Web App β
β (Container) β
β Port 5000 β
βββββββββββ¬ββββββββ
β
βΌ
βββββββββββββββββββ
β MySQL Database β
β (Container) β
β Port 3306 β
βββββββββββββββββββ
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature
) - Commit changes (
git commit -m 'Add amazing feature'
) - Push to branch (
git push origin feature/amazing-feature
) - Open a Pull Request