Skip to content

Commit fbc1756

Browse files
committed
feat: Fly.io deployment with PostgreSQL
- Add Dockerfile for multi-stage production build - Add fly.toml configuration - Update settings.py for Fly.io (DATABASE_URL, volumes) - Add dj-database-url and gunicorn to requirements - Create superuser and LinkedIn seeding management commands - Update README with mermaid architecture diagrams and badges - Remove Azure dependencies
1 parent 79125e1 commit fbc1756

9 files changed

Lines changed: 621 additions & 129 deletions

File tree

Dockerfile

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Stage 1: Build stage
2+
FROM python:3.11-slim as builder
3+
4+
WORKDIR /app
5+
6+
# Install build dependencies
7+
RUN apt-get update && apt-get install -y --no-install-recommends \
8+
build-essential \
9+
libpq-dev \
10+
&& rm -rf /var/lib/apt/lists/*
11+
12+
# Create virtual environment
13+
RUN python -m venv /opt/venv
14+
ENV PATH="/opt/venv/bin:$PATH"
15+
16+
# Install Python dependencies
17+
COPY requirements.txt .
18+
RUN pip install --no-cache-dir --upgrade pip && \
19+
pip install --no-cache-dir -r requirements.txt && \
20+
pip install --no-cache-dir gunicorn dj-database-url
21+
22+
# Stage 2: Production stage
23+
FROM python:3.11-slim
24+
25+
# Set environment variables
26+
ENV PYTHONDONTWRITEBYTECODE=1 \
27+
PYTHONUNBUFFERED=1 \
28+
PATH="/opt/venv/bin:$PATH" \
29+
PORT=8000
30+
31+
WORKDIR /app
32+
33+
# Install runtime dependencies only
34+
RUN apt-get update && apt-get install -y --no-install-recommends \
35+
libpq5 \
36+
&& rm -rf /var/lib/apt/lists/* \
37+
&& useradd --create-home --shell /bin/bash appuser
38+
39+
# Copy virtual environment from builder
40+
COPY --from=builder /opt/venv /opt/venv
41+
42+
# Copy application code
43+
COPY --chown=appuser:appuser . .
44+
45+
# Create directories for static and media files
46+
RUN mkdir -p /app/staticfiles /data/media && \
47+
chown -R appuser:appuser /app /data
48+
49+
# Collect static files
50+
RUN python manage.py collectstatic --noinput --clear
51+
52+
# Switch to non-root user
53+
USER appuser
54+
55+
# Expose port
56+
EXPOSE 8000
57+
58+
# Run gunicorn
59+
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "2", "--threads", "4", "--worker-class", "gthread", "--worker-tmp-dir", "/dev/shm", "--access-logfile", "-", "--error-logfile", "-", "blog.wsgi:application"]

README.md

Lines changed: 241 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,255 @@
11
# Eric Gitangu's Tech Blog
2+
3+
![Django](https://img.shields.io/badge/Django-5.1-green?style=flat-square&logo=django)
4+
![Python](https://img.shields.io/badge/Python-3.11-blue?style=flat-square&logo=python)
5+
![Fly.io](https://img.shields.io/badge/Deployed-Fly.io-purple?style=flat-square&logo=fly-dot-io)
6+
![PostgreSQL](https://img.shields.io/badge/Database-PostgreSQL-blue?style=flat-square&logo=postgresql)
7+
![WhiteNoise](https://img.shields.io/badge/Static-WhiteNoise-lightgrey?style=flat-square)
8+
![License](https://img.shields.io/badge/License-MIT-yellow?style=flat-square)
9+
210
<div align="center">
311
<a href="https://developer.ericgitangu.com">
412
<img src="https://developer.ericgitangu.com/_next/image?url=%2Ffavicon.png&w=96&q=75" style="border-radius: 50%" alt="deveric.io logo"/>
5-
<h1 align="center">Eric Gitangu</h1>
13+
<h2>Eric Gitangu</h2>
614
</a>
7-
A Django-powered blog showcasing insights in technology, cybersecurity, and software development. Built with Python and Django, featuring Azure cloud integration and robust security measures.
8-
9-
</div>
10-
11-
## 🚀 Features
12-
13-
- **Dynamic Content Management**: Built-in Django admin interface for content creation
14-
- **Cloud Storage Integration**: Azure Blob Storage for media and static files
15-
- **Production-Ready**: Configured for production with security best practices
16-
- **PostgreSQL Database**: Robust data storage with PostgreSQL
17-
- **Secure Authentication**: Comprehensive password validation and security middleware
18-
- **Logging System**: Detailed debug logging for monitoring and troubleshooting
19-
20-
## 🛠️ Tech Stack
2115

22-
### Backend Framework ⚙️
23-
- Django 4.x
24-
- Python 3.11
25-
- WSGI Application Server
16+
A Django-powered blog showcasing insights in technology, AI, systems architecture, cybersecurity, and software development.
2617

27-
### Database 🗄️
28-
- PostgreSQL with psycopg2-binary driver
29-
- Environment-based configuration
18+
**Live:** [blog.ericgitangu.com](https://blog.ericgitangu.com)
3019

31-
### Cloud Services ☁️ (Azure)
32-
- Azure Web Apps for hosting
33-
- Azure Blob Storage for static/media files
34-
- Azure Managed Identity authentication
35-
- Azure PostgreSQL database
36-
37-
## 💾 Storage Configuration
38-
- Whitenoise
39-
- PostgresSQL (Azure flexible)
40-
41-
### 📊 Monitoring and Logging
42-
43-
Debug logging configured for production monitoring:
44-
- File handler
45-
- Console handler
46-
- App-level logging - Default DEBUG
47-
48-
### 🔒 Security Configuration
49-
50-
- **CSRF Protection**: Configured for production domains
51-
- **Secure Middleware Stack**:
52-
- Security Middleware
53-
- WhiteNoise for static files
54-
- Session Management
55-
- CSRF Protection
56-
- Authentication
57-
- XFrame Options
58-
59-
### 🚀 Production Deployment
20+
</div>
6021

61-
The application uses GitHub Actions for CI/CD to Azure Web Apps. The deployment process includes:
62-
- Automated testing
63-
- Static file collection
64-
- Database migrations
65-
- Zero-downtime deployment
66-
- Azure Blob Storage configuration
67-
- Secure environment variable management
22+
---
6823

69-
For more details, see the deployment workflow in `.github/workflows/main_deveric-blog.yml`.
24+
## Features
7025

71-
## 🔗 Connect
26+
- **Dynamic Content Management**: Built-in Django admin interface for content creation
27+
- **Fly.io Deployment**: Production hosting with PostgreSQL and persistent volumes
28+
- **Production-Ready Security**: HTTPS, HSTS, secure cookies, and CSRF protection
29+
- **PostgreSQL Database**: Robust data storage with connection pooling
30+
- **WhiteNoise Static Files**: Compressed and cached static file serving
31+
- **Console Logging**: Streamlined logging for fly.io log aggregation
32+
33+
## Tech Stack
34+
35+
| Category | Technology |
36+
|----------|------------|
37+
| **Framework** | Django 5.1 |
38+
| **Language** | Python 3.11 |
39+
| **Database** | PostgreSQL (Fly Postgres) |
40+
| **Static Files** | WhiteNoise |
41+
| **Media Storage** | Fly Volumes |
42+
| **Server** | Gunicorn |
43+
| **Deployment** | Fly.io |
44+
45+
## Architecture
46+
47+
```mermaid
48+
flowchart TB
49+
subgraph Client ["Client Layer"]
50+
Browser[Web Browser]
51+
Mobile[Mobile Browser]
52+
end
53+
54+
subgraph Edge ["Fly.io Edge"]
55+
LB[Load Balancer]
56+
SSL[SSL/TLS Termination]
57+
end
58+
59+
subgraph App ["Application Layer"]
60+
subgraph Django ["Django Application"]
61+
WSGI[Gunicorn WSGI]
62+
MW[Middleware Stack]
63+
Views[Views & Templates]
64+
Admin[Admin Dashboard]
65+
Models[ORM Models]
66+
end
67+
68+
subgraph Static ["Static Files"]
69+
WN[WhiteNoise]
70+
CSS[CSS/JS Assets]
71+
end
72+
end
73+
74+
subgraph Data ["Data Layer"]
75+
PG[(PostgreSQL)]
76+
Vol[Fly Volume<br>/data/media]
77+
end
78+
79+
subgraph Models_Detail ["Data Models"]
80+
Post[Post]
81+
Author[Author]
82+
Tag[Tag]
83+
Comments[Comments]
84+
end
85+
86+
Browser --> LB
87+
Mobile --> LB
88+
LB --> SSL
89+
SSL --> WSGI
90+
WSGI --> MW
91+
MW --> Views
92+
MW --> Admin
93+
Views --> Models
94+
Admin --> Models
95+
Models --> PG
96+
Views --> WN
97+
WN --> CSS
98+
Models -.-> Vol
99+
100+
Post --> Author
101+
Post --> Tag
102+
Comments --> Post
103+
104+
style Django fill:#092E20,color:#fff
105+
style PG fill:#336791,color:#fff
106+
style Edge fill:#7C3AED,color:#fff
107+
```
108+
109+
### Component Interaction Flow
110+
111+
```mermaid
112+
sequenceDiagram
113+
participant U as User
114+
participant F as Fly.io Edge
115+
participant G as Gunicorn
116+
participant D as Django
117+
participant P as PostgreSQL
118+
participant V as Volume
119+
120+
U->>F: HTTPS Request
121+
F->>G: Forward Request
122+
G->>D: WSGI Handler
123+
124+
alt Static Asset
125+
D->>D: WhiteNoise serves
126+
D-->>U: Static file
127+
else Blog Post
128+
D->>P: Query posts
129+
P-->>D: Post data
130+
D->>V: Fetch media
131+
V-->>D: Image files
132+
D-->>U: Rendered HTML
133+
else Admin Action
134+
D->>D: Auth check
135+
D->>P: CRUD operation
136+
P-->>D: Confirmation
137+
D-->>U: Admin response
138+
end
139+
```
140+
141+
## Local Development
142+
143+
### Prerequisites
144+
- Python 3.11+
145+
- PostgreSQL (or use SQLite for local dev)
146+
147+
### Setup
148+
149+
```bash
150+
# Clone the repository
151+
git clone https://github.com/ericgitangu/blog.git
152+
cd blog
153+
154+
# Create virtual environment
155+
python -m venv venv
156+
source venv/bin/activate # On Windows: venv\Scripts\activate
157+
158+
# Install dependencies
159+
pip install -r requirements.txt
160+
161+
# Set environment variables
162+
export SECRET_KEY="your-secret-key"
163+
export DEBUG="True"
164+
export DBNAME="blog_dev"
165+
export DBUSER="postgres"
166+
export DBPASS="password"
167+
export DBHOST="localhost"
168+
export DBPORT="5432"
169+
170+
# Run migrations
171+
python manage.py migrate
172+
173+
# Create superuser
174+
python manage.py createsuperuser
175+
176+
# Run development server
177+
python manage.py runserver
178+
```
179+
180+
## Deployment to Fly.io
181+
182+
### Prerequisites
183+
- [Fly CLI](https://fly.io/docs/hands-on/install-flyctl/)
184+
- Fly.io account
185+
186+
### Deploy
187+
188+
```bash
189+
# Authenticate
190+
fly auth login
191+
192+
# Create app and Postgres
193+
fly apps create deveric-blog
194+
fly postgres create --name deveric-blog-db --region iad
195+
fly postgres attach deveric-blog-db --app deveric-blog
196+
197+
# Create volume for media files
198+
fly volumes create blog_data --region iad --size 1 --app deveric-blog
199+
200+
# Set secrets
201+
fly secrets set SECRET_KEY="$(python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())')" --app deveric-blog
202+
fly secrets set DEBUG="False" --app deveric-blog
203+
204+
# Deploy
205+
fly deploy --app deveric-blog
206+
207+
# Create superuser
208+
fly ssh console --app deveric-blog
209+
python manage.py create_superuser --username egitangu --email admin@ericgitangu.com --password yourpassword
210+
```
211+
212+
### Custom Domain
213+
214+
```bash
215+
fly certs add blog.ericgitangu.com --app deveric-blog
216+
fly ips list --app deveric-blog
217+
# Add A/AAAA records in your DNS provider
218+
```
219+
220+
## Project Structure
221+
222+
```
223+
blog/
224+
├── blog/ # Django project settings
225+
│ ├── settings.py # Configuration (Fly.io aware)
226+
│ ├── urls.py # URL routing
227+
│ └── wsgi.py # WSGI entry point
228+
├── portfolio/ # Main blog app
229+
│ ├── models.py # Post, Author, Tag, Comments
230+
│ ├── views.py # ListView, DetailView
231+
│ ├── admin.py # Admin customization
232+
│ └── templates/ # HTML templates
233+
├── Dockerfile # Multi-stage production build
234+
├── fly.toml # Fly.io configuration
235+
└── requirements.txt # Python dependencies
236+
```
72237

73-
- **Website**: [developer.ericgitangu.com](https://developer.ericgitangu.com)
74-
- **LinkedIn**: [Eric Gitangu](https://linkedin.com/in/ericgitangu)
75-
- **Azure Blog**: [deveric-blog.azurewebsites.net](https://deveric-blog.azurewebsites.net)
238+
## Security
76239

77-
## 📄 License
240+
- HTTPS enforced via Fly.io
241+
- HSTS with 1-year max-age
242+
- Secure session and CSRF cookies
243+
- Password validation policies
244+
- XFrame options protection
78245

79-
This project is licensed under the MIT License.
246+
## Connect
247+
248+
- **Portfolio**: [developer.ericgitangu.com](https://developer.ericgitangu.com)
249+
- **LinkedIn**: [linkedin.com/in/ericgitangu](https://linkedin.com/in/ericgitangu)
250+
- **GitHub**: [github.com/ericgitangu](https://github.com/ericgitangu)
251+
- **Blog**: [blog.ericgitangu.com](https://blog.ericgitangu.com)
252+
253+
## License
254+
255+
This project is licensed under the MIT License.

0 commit comments

Comments
 (0)