My Django Portfolio
My personal portfolio blog built with Django. It uses Postgres to store data, Tailwind for frontend styling, Docker for containerized deployment, and has asynchronous tasks with Celery + Redis. It fetches GitHub contribution data directly from the GitHub API and uses GitHub Actions for CI/CD.
The key feature is that the app pulls Markdown from GitHub README.MD files and shows it on blog pages, and Celery keeps this content updated in the background.
Project Structure
my_django_portfolio/
|- django/
| |- config/ # Project config
| |- entrypoints/ # Container startup scripts
| |- main/ # Core app
| | |- models.py
| | |- views.py # Page/API views
| | |- tasks.py # Celery tasks
| | |- templates/ # Django templates
| | |- static/ # CSS/JS/images
| | `- tests/
| `- manage.py
|- docs/ # Technical documentation
|- infra/
| |- Caddyfile
| |- docker-compose.dev.yml
| |- docker-compose.prod.yml
| |- Dockerfile
| `- scripts/ # .env and status helper scripts
|- Makefile
|- package.json
|- pyproject.toml
`- README.md
Features
- Portfolio project listing and detail pages
- Blog routing by slug (
/blog/<slug>/) - Tag and status-based project organization
- Responsive UI built with Tailwind CSS + DaisyUI
- Visitor counter middleware and profile-driven site metadata
- Background jobs with Celery worker + beat scheduler
- Scheduled markdown synchronization (
main.tasks.sync_project_markdowns_task) - GitHub contribution heatmap data endpoint (
/about/heatmap-data/) with cached snapshot storage
Tech Stack
- Python 3.13, Django 5.1
- PostgreSQL, Redis
- Celery
- Tailwind CSS v4 with DaisyUI
- Gunicorn, Caddy
- Docker Compose
Showcase
Home page
A hero banner with floating tech keywords in the background. Below it, a project timeline split into Planned, Ongoing, and Finished sections — each collapsible.
Projects page
A filterable grid of projects. You can search by name or filter by technology tags like Django, Python, Docker, etc. Each card shows a project thumbnail, description, status badge, and a "View Project" button.
About page
The main landing page showing a brief intro ("Hi, I am John Doe"), tech stack badges, current learning goals, and a live GitHub contributions heatmap fetched directly by Django from GitHub.
Dark theme
All backgrounds, text, and cards switch to dark colors. The accent color shifts to green.
About when logged as admin (page owner)
The about page with extra admin controls visible, including a Scheduled Jobs panel showing Celery tasks (heatmap refresh, markdown sync) and an Executed Tasks panel with task history and run timestamps.
Quick Start (Docker)
Development
make dev-up
make dev-status
make dev-down
Open http://localhost:8000.
make dev-up creates .env from .env.example when needed.
Production
make prod-up
make prod-status
make prod-down
make prod-up requires an existing .env file and fails if it is missing.
Local Development
Prerequisites
- Python 3.13+
- PostgreSQL
- Redis (required for Celery worker/beat)
- Node.js + npm
Setup
cp .env.example .env
uv sync --extra dev
npm install
npm run build:css
Run app and workers
uv run python django/manage.py migrate
uv run python django/manage.py collectstatic --noinput
uv run python django/manage.py runserver
In separate terminals:
uv run celery -A config worker --workdir=django --loglevel=info
uv run celery -A config beat --workdir=django --loglevel=info
Open http://localhost:8000.
Verification Commands
make verify
make verify expects the dev dependencies installed through uv sync --extra dev.
Heatmap Configuration
- Set
GITHUB_HEATMAP_TOKENin.envto enable the portfolio heatmap. - Django fetches the configured token owner and contribution calendar directly from GitHub and caches the normalized payload in
HeatmapSnapshot. - GitHub login/auth integration is not part of this project.
Documentation
- Architecture details:
docs/architecture.md - Implementation details:
docs/implementation.md - Frontend diagram:
docs/frontend.md
Troubleshooting
- Caddy TLS for
wwwfails (ERR_SSL_PROTOCOL_ERROR): set bothAPP_DOMAINandAPP_WWW_DOMAINin.env(for example,APP_DOMAIN=krystianstasica.pl,APP_WWW_DOMAIN=www.krystianstasica.pl). - Docker socket permission error: add your user to the Docker group or run with elevated privileges.
- CSS not updating: run
npm run build:cssand verify input/output paths inpackage.json. - SELinux bind mount issue with Caddyfile: use
:Zrelabel option (already configured indocker-compose.prod.yml). 502 Bad Gatewayafter changing DB name: if you changeDOCKER_DB_NAME, create that database in PostgreSQL first, otherwisewebcan stay inwait-for-db.- Markdown sync looks successful but files are not visible in app: ensure
workermountsmedia_volume:/app/media. - Permission errors on static/media/celerybeat files in Docker: app containers run as non-root (
10001:10001), so existing volumes may need a one-time ownership fix (chown). UnicodeDecodeErrorin templates/content: save text files as UTF-8.
License
This project is licensed under the MIT License. See LICENSE.