Django Portfolio – Project Goals & Requirements

Django Portfolio – Project Goals & Requirements

Requirements and scope for my self-hosted Django portfolio: a low-cost DevOps lab that showcases my work, with clear user features, technical constraints, and on-prem infrastructure choices.
Created: 2025-11-06
Updated: 2025-12-01
DevOps IT Management

Why I’m Building This Portfolio?

Most people use LinkedIn to present their professional profile, and I do too. It already shows my roles, education, and certifications well enough. This project is something different.

I am building this portfolio as a live DevOps laboratory: a place where the full development lifecycle is visible, from idea and design through implementation, deployment, and refactoring. Instead of only showing polished end results, I want to document how I explore technologies, how I make architectural decisions, and how I correct them when reality proves me wrong.

This portfolio is therefore:

  • A research log
    I experiment with different tools (frameworks, databases, CI/CD patterns, observability stacks) and record how they behave in real conditions, on real infrastructure.
  • An architecture playground
    I try out different ways to structure applications, handle environments, manage databases, and design deployment strategies, and I keep the reasoning and trade-offs visible.
  • A safe place to fail
    Not every idea will be “right” the first time. This project intentionally leaves room for mistakes, rollbacks, and redesigns, because that is how I evolve. I need to try → fail → reconsider → do research → try again → do it better.
  • A continuous improvement loop
    Each iteration is an opportunity to ask, “How could this be done better?” and then implement, verify, and document the improvement.

So while it does function as a long-term technical portfolio, its main goal is broader:
to show how I think and work as a DevOps-minded engineer, not just what I have already finished. It is a running narrative of trials, failures, and lessons learned, all anchored in a real, self-hosted system that I maintain and evolve.

Self-hosted, low-cost infrastructure by design

A key requirement for this portfolio is that it must be fully self-hosted, with infrastructure costs kept as close to 0 SEK as possible.

In practice, this means:

  • No managed cloud runtime
    No dependency on managed cloud platforms to run the application.
  • No “temporary” free credits
    No use of free cloud credits that would later turn into recurring costs.
  • On-prem hardware only
    Everything runs on existing on-prem hardware (Synology NAS and home lab) using containers and virtualisation.

This is not only about saving money. It reflects how I think as both a DevOps engineer and an IT manager:

  • Cost awareness at small scale
    Even for a personal project, infrastructure choices compound over time. A few euros per month per service quickly becomes real money over years. Designing for near-zero cost forces me to think in terms of total cost of ownership, capacity, and consolidation.
  • On-prem and virtualisation practice
    Many organisations still run hybrid or on-prem environments. By hosting this portfolio on my own NAS, I can practice the same skills required in those environments: resource-constrained optimisation, storage layout, backup strategy, and network configuration, instead of relying on “infinite” cloud resources.
  • No hidden subsidies
    Free cloud credits hide the real price of architectural decisions. For this project, I want a realistic view: if a design cannot be sustained without subsidies, it is not appropriate for a long-running personal system.
  • Long-term sustainability
    The portfolio is intended to run for many years in the background. By anchoring it to hardware I already own and keeping recurring costs close to zero, I reduce the risk that I will have to shut it down or move it because of monthly fees.

In other words, the hosting model is part of the learning goal. The portfolio is not only a showcase of code and infrastructure patterns, but also an exercise in running a small but realistic service with strict cost constraints, using on-prem, virtualised infrastructure that I operate end to end.

Portfolio user & content requirements

From a visitor and content-owner perspective, the portfolio must support:

  • Bilingual content (SV/EN)
    All main pages and key texts available in both Swedish and English, with easy language switching.
  • Editable via admin
    Content (texts, projects, metadata) managed through an admin interface, without changing code.
  • File uploads & downloads
    Ability to upload CVs, certificates, diagrams, etc., and show clear, structured download links on relevant pages.
  • Clean, simple UI
    Minimalistic design, readable typography, and straightforward navigation focused on core sections like About, Projects, and Contact.
  • Straightforward navigation
    Clear menu structure, no deep nesting, and fast access to the most important sections.
  • Contact form
    Simple, reliable form for visitors to reach me without exposing my email address directly.
  • External links
    Support for linking out to GitHub, LinkedIn, and other resources, clearly indicated as external.
  • Code snippets display
    Support for nicely formatted code blocks (dark/light theme aware) to showcase examples, configurations, and infrastructure snippets.

Core architecture

Separate application & database layers

  • The Django application runs in its own container, behind gunicorn, with static and media files served by the separate nginx-proxy project.
  • The database layer lives in the dedicated db-stack project on Synology Container Manager and includes:
    • PostgreSQL containers for portfolio_dev and portfolio_prod
    • pgAdmin UI for easier DB administration
    • A PostgreSQL backup container/image that runs scheduled pg_dump jobs and stores dumps on NAS volumes
  • Application and database communicate over an internal Docker network only; the database is never exposed directly to the internet.
  • Each layer has its own lifecycle: the app can be rebuilt and redeployed without touching the database containers, and DB maintenance (upgrades, backups, restores) can be done independently of the app.

Distinct dev and prod environments

  • Separate databases:
    • portfolio_dev for local experiments, schema changes, and testing
    • portfolio_prod for the live portfolio, with stricter settings and access control
  • Environment-specific settings:
    • Django uses different settings modules for dev and prod (e.g. settings_dev / settings_prod), controlled via environment variables.
    • Dev enables debug tooling and relaxed email/logging; prod has DEBUG off, stricter security, and production logging.
  • Data flows:
    • Regular backups from portfolio_prod are created by the backup container.
    • When needed, a sanitized snapshot of prod can be restored into portfolio_dev so new features can be tested against realistic data without risking production.
  • Deployment separation:
    • GitHub Actions with the self-hosted GitHub runner build and push images to the self-hosted Docker registry.
    • A prod deployment job pulls images from the registry and restarts only the production app container; dev can be updated separately using its own compose file or manual pulls.

Self-hosted, containerised runtime

  • Everything runs on-prem on the Synology NAS / home lab using Docker, divided into multiple Compose projects in Synology Container Manager for clarity and separation of concerns:
    • docker-registry – private Docker registry + UI for managing images
    • watchtower – automatically monitors and updates containers when new images are available
    • db-stack – PostgreSQL (dev/prod), pgAdmin, and backup container
    • observability – Netdata for metrics and basic health checks
    • nginx-proxy – reverse proxy for Django, static/media offload, and integration with Cloudflared
    • github-runner – self-hosted GitHub Actions runner for CI/CD
    • portfolio-app – Django 5 + Gunicorn container running the actual portfolio site
  • External access is provided via an nginx-proxy + Cloudflared tunnel chain:
    • Cloudflared terminates at the NAS and forwards traffic to nginx-proxy, which routes it to the portfolio-app container over an internal network.
  • The frontend uses Bootstrap and vanilla JavaScript inside the Django app, with static assets collected to NAS volumes and served directly by nginx-proxy.
  • Netdata watches host and container metrics, giving a lightweight observability layer without any managed cloud services.

Together, this architecture keeps application, database, networking, CI/CD, and observability clearly separated, fully self-hosted, and aligned with the low-cost, on-prem DevOps lab goal.

Tech stack

  • Backend: Django 5, Python 3.11
  • Frontend: Bootstrap, vanilla JavaScript
  • Database: PostgreSQL (separate dev and prod instances)
  • DB tooling: pgAdmin UI for easier database management
  • DB backups: Dedicated PostgreSQL backup container/image (e.g. pg_dump based), scheduled and stored on NAS
  • Runtime: Docker, Gunicorn, nginx-proxy (reverse proxy & static/media)
  • Image management: Self-hosted Docker registry with web UI
  • Automation: Watchtower for automatic container image updates
  • Orchestration: Multiple Docker Compose projects in Synology Container Manager
    • docker-registry (registry + UI)
    • watchtower
    • db-stack (PostgreSQL, pgAdmin, backup)
    • observability (Netdata)
    • nginx-proxy
    • github-runner
    • separate project for the Django portfolio app
  • Hosting: Synology NAS / home lab, on-prem only
  • CI/CD: GitHub Actions with a self-hosted GitHub runner on the NAS
  • Networking: Cloudflared tunnel for secure external access
  • Monitoring: Netdata (or similar) for basic metrics and health checks

Tooling & automation

  • AI-assisted workflow
    Use tools like Lovable (for UI/layout ideas) and Claude Sonnet / other LLMs (for coding support, refactors, and documentation) as part of the normal development process, not just as one-off experiments.
  • Self-hosted GitHub runner
    CI/CD pipelines execute on a runner hosted in my own environment, building images, running tests, and deploying to the NAS.
  • Scripted operations
    Common tasks (migrations, backups, seed data) are scripted so they can be run repeatably and safely, instead of as ad-hoc manual commands.

Security & reliability

  • Basic WAF & protection
    Explore using Cloudflare rules and/or other WAF options to protect the portfolio, at least for admin and contact form endpoints.
  • Simple observability
    At minimum: structured logs, container health checks, and a way to inspect basic metrics (Netdata or similar). Over time, experiment with more advanced monitoring options if needed.
  • Backups & recovery
    Regular backups of PostgreSQL databases and media files, stored on the NAS (and optionally replicated elsewhere), with occasional restore tests to verify that the site can be recovered after a failure.
  • Secrets management
    Application secrets (Django secret key, database credentials, mail provider keys) are stored in environment variables and/or GitHub Secrets, not in the repository.

Scope & non-goals

This portfolio is intentionally limited in what it tries to do. It should be usable, easy to read, and technically solid, but not a design showcase.

In scope

  • Single-user admin (only I manage content)
  • Bilingual content (SV/EN)
  • CI/CD from GitHub to Synology NAS
  • On-prem, containerised hosting
  • Basic observability and logging
  • Backup and recovery strategy
  • Clean, readable layout with straightforward navigation

Out of scope

  • Multi-tenant user accounts or logins
  • Heavy analytics or complex tracking
  • Full enterprise CMS features (workflows, roles, content versioning, etc.)
  • High-traffic optimisation and global scaling
  • Mobile app or separate SPA frontend
  • Advanced, highly interactive UI or “pixel-perfect” design work (complex animations, heavy JS frontends, etc.)

Redundancy is out of scope for implementation:
With a single on-prem NAS, the system will not have built-in high availability or automatic failover.

  • Single point of failure is accepted
    I accept occasional downtime in case of hardware issues, power outages, or internet failures to keep infrastructure cost low.
  • Cost is the main constraint
    Adding true redundancy (second NAS, multi-site setup, cloud mirror) would significantly increase cost and complexity, which goes against the “near 0 SEK” goal.
  • Redundancy treated as an architecture exercise
    Instead of implementing it now, I plan to explore and document possible architectures to improve reliability in the future (e.g. secondary node, offsite backups, read-only mirror), including their cost and trade-offs).

Learning objectives

With this project I want to:

  • Practice designing and operating a self-hosted, containerised Django application.
  • Explore cost-aware architecture choices on small, constrained hardware.
  • Use AI tools (Lovable, Claude Sonnet, etc.) as an integrated part of my development workflow.
  • Document failures, redesigns, and improvements as a normal part of the lifecycle, not something to hide.
  • Strengthen my DevOps mindset by treating even a personal portfolio as a small, real service with environments, CI/CD, observability, and security considerations.