Get in touch!

Building the site in one day

By Ram Parameswaran Static website Wagtail CMS One-day-build! Personal Blog


It's the site you're on right now!

Today I am building and deploying a static website to showcase my portfolio and blog about my side projects. Yep, that's right - design; django boilerplate; back-end; templates and styles; content; and deployment ... in 8 hours. Let get started!

note - I've found this post to be a useful directory for all those resources I use when launching and deploying a Django project (e.g. how do I set up nginx config files again?). I hope you do too!


The site will be a static website, but I will be updating blog and portfolio content periodically. I am using Django (of course) and the Wagtail CMS. Wagtail is great for drafting and uploading rich (predominantly text) content. My aim is to not have to touch the codebase ever again after today. This reminds me of a paragraph from the Wagtail docs (see "the zen of Wagtail"):

The key to using Wagtail effectively is to recognise that there are multiple roles involved in creating a website: the content author, site administrator, developer and designer ... Either way, it’s important to be aware of which of those hats you’re wearing at any moment, and to use the right tools for that job.

Today I am all of the above. But from tomorrow onwards, I'm only the content author.

9:20am - Create Django Project

The first step in any project is to make a virtual environment. I find virtualenvwrapper on Ubuntu quick and easy. Unfortunately I ended up hitting pip install errors because I didn't specify which Python version I wanted and it defaulted to Python2 *facepalm*. For next time, the command is:

pip install cookiecutter
mkvirtualenv [env_name] --python=/usr/bin/python3.7

I like Pydanny's django cookiecutter because of its folder structure and awesome Django boilerplate (especially all those and .gitignore parameters)! See 2 Scoops of Django for more reasons why you should use cookiecutters.

Admittedly Pydanny's cookiecutter is overkill for this project. We have no user auth (other than the basic Django superuser), minimal database requirements (sqlite will do!), no docker, no celery, no third-party static file hosting. Nevertheless, it is good form, and every minute saved is profit.

9:50am - git repository and initial commit

Now that I've got some boilerplate to upload, and my .gitignore is already configured, it's time to create a git repo and make the initial commit. I always make sure to clean the file of sensitive info (e.g. SECRET_KEY) before committing to version control.

At this point I like to set out the intended layout of the This will form the basis for the project's documentation and eventual handover/onboarding for new developers. Documentation is easiest when planned in advance! Here is a great cheat-sheet for github markdown syntax.

Next I update the Django files. Changes include:

  • switching database back-end to sqlite, which is more than sufficient for this site;
  • adding Wagtail and Django-taggit to INSTALLED_APPS (and requirements.txt too!);
  • removing unused apps from INSTALLED_APPS ('all-auth' and 'rest-framework' in this case);
  • making SECRET_KEY read from a .txt file that is not tracked by version control

10:25am - Site structure and wagtail page models

I'll be the first to admin, I'm not a graphic designer. For most of my projects I am provided designs, wireframes, graphics, etc.

For this site I've taken 'inspiration' from several other developers' websites. As I don't like flashy front-end-animations, I've decided to adopt a simple long-scrolling UI similar to:

The site will have two pages: i) a long-scrolling 'landing page' highlighting my portfolio; and ii) a 'blog' index page with 'blog-post' child pages.

Landing Page

The 'landing page' will have 3 sections:

  1. a banner - with title, avatar, and call-to-action button);
  2. an 'about me' - with text and photo;
  3. my portfolio - with cards for each project in my portfolio.

This may sound overly descriptive, but it relates directly to how I structure each component in Wagtail CMS:

  • 'HomePage' which is the base Page model;
  • 'PortfolioItem' which is an orderable objects linked to HomePage;
  • 'PortfolioItemTag' which is a necessary model field to store tags.

Always think about how the content will be presented before you think of your schema!


The 'blog' will have an index page and individual item pages for each blog post. Wagtail makes this whole endeavour easy. The index is found at /blog/, and the child pages at /blog/<slug>/. I don't even have to define the re_path or create views for these - it's all handled by the Wagtail Page model. Profit!

11:30am - Coffee time

Most important time of the day!

12:00pm - Django templating

Now that I've set up the Wagtail model fields, it's time to start marking up the templates!

In previous projects I've relied on the Bootstrap 4 grid system for making responsive layouts. Let me say in no uncertain terms, the Bootstrap grid system is gross. It results in eye-wateringly complex nested <div>'s and hacky nonsense to override the default CSS specificity. This point reminds me of a great conference talk by Morten Rand-Hendriksen on why CSS frameworks are garbage and how we can move to something better.

Thanks to Wagtail, the Django html templates are actually quite concise. Just a collapsible Bootstrap navbar at the top, containers for wagtail blocks, and an empty footer.

2:00pm - CSS formatting

I've been hunting for inspiration for a page theme. I've decided on 'europa' font-face, and a pastel colour theme - quite bold for me!

I wanted to avoid the Bootstrap4 grid system this time, in favour of the CSS Grid. ... Unnnfortunately the time-constraint got the better of me and I've fallen back to the hated-but-known Bootstrap 4.

6:00pm - Domain and web hosting

As expected, the templating and CSS took a while!

Now I'm moving onto deployment. I bought a domain name ( from Google domains. I find the Google domains process much easier than Godaddy.

For the webserver, I'm going to use one of my existing DigitalOcean instances. This article provides a great summary of the process. Essentially its identical to a standard deployment with Gunicorn and Supervisor. The only difference is in the nginx config, because I'm serving two django applications (with two different domain names) from the same server/IP address. To manage this I'm creating a 'virtual server configuration' for each domain that will redirect the request to the corresponding Gunicorn process.

7:00pm - Final touches

I had to do some fiddling around with Nginx HTTPS settings because it was creating an infinite redirect loop. This trick did it just nicely!

Lastly, some final touches (a basic favicon, customise the <head> tags) and et voila, we're done! Not bad for a day's work!

Ram Parameswaran

about Ram Parameswaran

Django & React Developer, Data Geek and Maker-Enthusiast.
Ram builds web applications that help businesses operate more efficiently.