Blog and News Run In-House — a Self-Built CMS for the Rental Site

An admin dashboard so the operators themselves can update blog, news, FAQ, and images without an external CMS

CMSin-house operationblognewsadmin dashboard
5 min read

Introduction

Launching the site isn't the finish line. Seasonal event info, nearby shops opening and closing, new questions to add to the FAQ — for a rental property site, the real work starts after launch.

Going back to an agency every time means paying for it in both money and waiting time. In this article we'll walk through how we built an in-house admin dashboard so the operators can update the content themselves.

What belongs in the dashboard, and what doesn't

Draw the line at update frequency

Should something be editable from the dashboard, or should it live in a configuration file? We settled that question with a single test: how often does it actually change?

Making everything editable from the dashboard doesn't just inflate development cost — it also creates the risk that someone edits the parts nobody should touch. Accidentally wiping the cancellation policy would be a genuine disaster.

That's exactly why we made the email copy read-only. The dashboard lets you preview the wording, and any change goes through the development side.

How the dashboard is laid out

The dashboard is split into two zones: operations and content.

Dashboard layout
Admin dashboard

The landing point after login

Operations

Checking bookings and payments / issuing guest-only pages / previewing email copy / referencing operational workflows

Content

Blog / news / FAQ / images / nearby spots

Handling today's bookings and sitting down to write content put you in completely different headspaces. Keeping them in separate zones instead of mixing them on one screen made the dashboard much easier to navigate.

How we protect the dashboard login

Two-step authentication

Anyone on the internet can open the dashboard URL. So we made authentication a two-step affair.

Logging in to the dashboard
Browser-level authentication

The moment you reach the dashboard URL, you're asked for a username and password

Enter an email address

We check whether the address is on the allow list (the screen looks identical either way)

Receive a 6-digit code

Enter the code sent by email. Valid for 10 minutes, five attempts

Session issued

Valid for 8 hours, and re-verified on every action from that point on

We deliberately avoided a system where people have to remember a password, because passwords get reused and leaked. Instead: if you can receive the email, you can get in. Simple as that.

Keeping the "who gets in" list in environment variables

The list of email addresses allowed to log in lives in environment variables (server-side settings), not in the database.

There's a reason for that. If the list lived in the database, anyone who managed to break into the database could simply add themselves as an administrator. With environment variables, only someone who can change the server configuration can touch it. And when a staff member moves on, removing them from the settings instantly invalidates all their sessions.

Getting past the login screen is not a permission

Making it through the login screen isn't a reason to allow every action that follows. For individual operations — deleting an article, issuing a guest-only page — we re-check permissions every single time.

We also split roles into owner, administrator, editor, operations staff, and view-only, so we can allow someone to write articles without letting them see booking details.

Keeping an audit trail

Who did what, and when — all of it is recorded. Creating, updating, and deleting articles, uploading images, issuing guest-only pages. The point is to be able to trace "when did this start going wrong" if trouble hits.

That said, the log never stores the sensitive data itself. We record the fact that "a guest-only page URL was reissued," but not the URL that was issued.

Three sub-topics

Safe text editing

An editor that lets you write freely is also, left unchecked, a doorway for dangerous HTML. How we balanced editing freedom with safety is covered in "A Rich-Text Editor with Safe HTML."

Automatic image optimization

Upload a photo straight from your phone and you've just put a multi-megabyte file on the site. The mechanism that resizes and compresses automatically is explained in "An Optimized Image Upload Pipeline."

Publishing flow and multiple languages

Drafts, scheduled publishing, and rolling content out into four languages. The publishing setup that makes day-to-day operation easier is covered in "Drafts, Scheduled Publishing, Multilingual."

Wrapping up

Three things mattered most in designing this in-house CMS.

  1. Draw the line at update frequency — only frequently changing things go in the dashboard; the untouchables stay in config files
  2. Two-step authentication, permissions checked every time — getting past a screen is never a permission in itself
  3. Keep the admin list in environment variables — so nobody can add an administrator through the database