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 also has optional integration with my other project FastAPI to get my GitHub contribution data. It uses GitHub Actions for CI/CD and is deployed on Digital Ocean droplet.
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.
My Django Portfolio works with github-heatmap
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
|- Caddyfile
|- docker-compose.yml
|- Dockerfile
|- 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) - Optional FastAPI heatmap integration (
/heatmap/me) 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 pulled from a FastAPI backend.
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 — a "Disconnect GitHub" button, 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)
Run
#check variables, use good secrets
cp .env.example .env
docker compose up --build -d
Open https://localhost.
Stop services:
docker compose down
Local Development
Prerequisites
- Python 3.13+
- PostgreSQL
- Node.js + npm
Setup
cp .env.example .env
uv sync
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
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.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.