All topics
The network drive
Files and websites
Cameras and sensors
Network and questions
Template Sites
Plain index.html folders are enough for many sites,
but blogs and multi-page sites want layouts, [Markdown](https://commonmark.org/) and a post list. NAS
Reborn renders these the way [Jekyll](https://jekyllrb.com/) does, live on the device, with no build
step.
Turning a folder into a template site
A public folder becomes a template site the moment a _config.yml
file exists at its root. From then on, pages that begin with front matter,
two --- lines around a little YAML, are rendered through
templates before serving:
---
title: Hello from the drawer
layout: default
---
This page is written in **Markdown** and served by an old phone.
Everything else, images, stylesheets, plain HTML without front matter, is served as-is.
The Jekyll model
- Layouts live in
_layouts/; a page picks one withlayout: defaultin its front matter, and the layout places the page's body with{{ content }}. Layouts can nest. - Includes in
_includes/hold shared fragments like headers and footers. - Posts are files named
_posts/2026-07-24-my-title.md; date and slug come from the filename, andsite.postslists them newest first:
{% for post in site.posts %}
<a href="{{ post.url }}">{{ post.title }}</a> — {{ post.date | date: "%b %-d, %Y" }}
{% endfor %}
- Liquid is the template language, the same tags and the familiar
Jekyll filters (
markdownify,date,where,group_by,jsonifyand dozens more) used by Jekyll and GitHub Pages, safe by design for untrusted templates. - Templates see the familiar globals:
site(configuration, posts, tags, categories,site.data.*from_data/files, andsite.static_fileswith image sizes),page(the current page's front matter) andcontent(inside layouts).
Because the model matches Jekyll, an existing Jekyll blog largely serves as-is: drop the folder in and it renders.
The device object
Templates additionally get a device object with live facts about
the hardware doing the serving: device.name,
device.marketing_name, device.os.name and
.version, device.battery.level and
.state, device.storage.total, .free
and .used, device.thermal_state,
device.uptime (whole minutes) and device.uptime_text
(the same in words: "110 minutes", "4 hours", "3 days"). A footer like
Served by {{ device.marketing_name }} on battery {{ device.battery.level }}%
is rendered fresh whenever the facts change, and pages that skip
device stay fully cached.
Sensor readings arrive the same way, through device.sensors for
the device's own instruments and a home object for HomeKit
accessories you have picked. See
Home and Sensor Data.
JSON endpoints
Rendering is decided by front matter, not by file extension, so a file that
begins with a --- fence is rendered whatever it is called, and
its name decides the content type it is served with. That makes an API
endpoint an ordinary page:
---
layout: null
---
{"temperature": {{ home.hallway.temperature }}, "uptime": {{ device.uptime }}}
Save that as readings.json and any script or dashboard can
fetch it. There is no separate API to configure; the same caching and the
same access level apply as to every other page in the folder.
Live updates
Any page can also be followed instead of polled. Request the same URL with
an Accept: text/event-stream header and the connection stays
open, sending a fresh render whenever anything the page actually read
changes:
const events = new EventSource("/sensors/readings.json");
events.onmessage = event => update(JSON.parse(event.data));
Because the server tracks exactly which files and facts each page touched, nothing is sent while nothing has moved. A device serves up to 16 of these streams at once, and a browser reconnects on its own if it is turned away.
Example sites to start from
The Content tab's Add example menu builds a working site from your own material:
- a Blog from text or Markdown files,
- a Download site listing files you pick,
- a Gallery from photos and videos, with thumbnails generated on the device,
- and a Sensor Dashboard with a tile per accessory, a
readings.jsonendpoint and tiles that update themselves over a live stream.
Each example is created private, or protected where it shows readings, so you can look around and edit before deciding who else should see it. A per-folder Keep files updated switch, off by default, lets a newer version of the app refresh the parts of an example you have not edited; files you have changed stay yours, and nothing is ever deleted. The folder's screen also offers a one-off Update and a confirmed Reset.
Editing and publishing
There is no deploy step: save a file and the next request serves the new version. Edit directly in the Files app, sync the folder with any S3 client, or keep the site in a git repository on the device and publish by pushing to it. S3 clients always see the raw source files, never rendered output, so a synced site round-trips exactly.
Static site export
The folder's detail screen can also export the rendered site: every page and post rendered to plain HTML with root-relative links, images with metadata stripped, and hidden files left out, delivered as a folder through the share sheet. Use it to host a copy anywhere a plain folder of files can live, or as a frozen snapshot of a site. Exporting is part of the Pro unlock.