Installation
Requirements, server setup, and getting started.
Why lazysite
Drop .md files in your docroot and they are served as fully rendered
HTML pages - no build step, no CMS, no database. Pages are generated on
first request and cached as static HTML. Content is portable, version
control friendly, and works with any deployment workflow.
For the full motivation behind lazysite, see Motivation.
Web server support
lazysite runs on its own built-in server, or on any standard web server via CGI and error-handler mechanisms.
- Built-in server -
perl tools/lazysite-server.plruns the full processor on a non-privileged port with no configuration; ideal for development, previews, and small internal or home use - Apache 2.4 - supported, HestiaCP installer provided
- Apache without HestiaCP - configure
FallbackResourcemanually - Nginx - use
error_page 403 404to point to the CGI script - Any web server with CGI support and configurable error handlers should work
If you use a front end other than the shipped Apache, nginx or built-in server and you intend to protect static files with ACLs, read Your web server has to co-operate in Authentication. A web server that answers an existing file from disk never reaches lazysite, so a per-file access rule cannot apply - and the failure is silent. Sites that do not use ACLs are unaffected and need nothing.
Requirements
- Apache 2.4 with CGI support and
FallbackResourceconfiguration - Debian / Ubuntu (or any Linux with the Perl modules below)
libtext-multimarkdown-perllibtemplate-perllibwww-perl(for remote.urlsources and oEmbed)JSON::PP(Perl core - no separate install needed)
Optional:
libtemplate-plugin-json-escape-perl(required for the search index)
HestiaCP is supported with a dedicated installer. For other environments see the manual installation section below.
Installation
You install the package; you provision a site
Two different acts, and lazysite keeps them apart on purpose:
| Act | What it touches |
|---|---|
| Installing lazysite on a host | the engine payload and the lazysite command. No site tree is touched. |
| Provisioning a site | one domain's docroot and cgi-bin. |
So a host with lazysite installed and no sites provisioned is a normal state, and installing a newer lazysite never changes a site by itself.
The site commands
Three verbs, and you say which one you mean. None of them works out your intention from the state of the disk, and each refuses rather than quietly doing one of the others:
lazysite provision --docroot /path/to/public_html --cgibin /path/to/cgi-bin
lazysite upgrade --docroot /path/to/public_html
lazysite reinstall --docroot /path/to/public_html
| Verb | Use it when | It refuses when |
|---|---|---|
provision |
the site does not exist yet | something is already installed there |
upgrade |
the site should move to this version | nothing is installed, or the site is already at this version |
reinstall |
this version's files were edited or lost and should go back to what the release ships | the versions differ — that is an upgrade |
reinstall leaves content, accounts and configuration alone. It is not
repair, which
touches no files at all.
Run them as the site's own user, never with sudo: they refuse root, so
that nothing in a site tree ends up owned by root. Each verb reads
release-manifest.json from the payload and records what it installed at
{docroot}/lazysite/.install-state.json.
From an unpacked tarball
There is no lazysite on PATH until the package is installed, so run the
command out of the tree you unpacked. It finds its own payload:
tar xf lazysite-<version>.tar.gz
cd lazysite-<version>
perl tools/lazysite-cli.pl provision \
--docroot /path/to/public_html --cgibin /path/to/cgi-bin
The verbs and their options are identical. A tarball is how the code reaches the host; it is not a different way to install a site.
install.sh in the tarball root installs nothing. It is kept only to point
anyone who still types it at the three verbs above.
HestiaCP
lazysite registers itself as a HestiaCP web template, so a domain is set up
from the control panel and the engine is deployed on rebuild. Install the
lazysite-common and lazysite-hestia packages, copy the templates into
Hestia's template directories, then per domain:
- Edit your domain
- Set the web template to
lazysite-cgi(orlazysite-fcgi) - Save and rebuild
The full sequence, including the nginx proxy layer and the per-domain
onboarding command, is in installers/hestia/INSTALL-RUNBOOK.md in the
release.
Manual Apache installation
For Apache without HestiaCP, install the Perl dependencies and configure the vhost manually:
apt install libtext-multimarkdown-perl libtemplate-perl libwww-perl
Copy lazysite-processor.pl to your cgi-bin/ directory and make it executable:
cp lazysite-processor.pl /var/www/example.com/cgi-bin/
chmod 755 /var/www/example.com/cgi-bin/lazysite-processor.pl
Copy the starter files to your docroot:
mkdir -p /var/www/example.com/public_html/lazysite/templates/registries
mkdir -p /var/www/example.com/public_html/lazysite/layouts
mkdir -p /var/www/example.com/public_html/lazysite-assets
cp starter/lazysite.conf.example /var/www/example.com/public_html/lazysite/lazysite.conf
cp starter/lazysite/templates/registries/*.tt /var/www/example.com/public_html/lazysite/templates/registries/
mkdir -p /var/www/example.com/public_html/lazysite/templates/system
cp starter/lazysite/templates/system/*.md /var/www/example.com/public_html/lazysite/templates/system/
cp starter/index.md /var/www/example.com/public_html/
Add to your Apache vhost configuration:
DirectoryIndex index.html index.htm
FallbackResource /cgi-bin/lazysite-processor.pl
<Location /lazysite>
Require all denied
</Location>
<Directory /var/www/example.com/public_html>
Options -Indexes +ExecCGI
AllowOverride All
</Directory>
Ensure the web server user can write to the docroot:
chown ispadmin:www-data /var/www/example.com/public_html
chmod g+ws /var/www/example.com/public_html
The setgid bit (s) ensures new subdirectories created by the processor
inherit the www-data group automatically.
Getting started
Local development
Clone the repository and run the built-in development server:
git clone https://github.com/OpenDigitalCC/lazysite.git
cd lazysite
perl tools/lazysite-server.pl
Open http://localhost:8080/ to browse the starter site. No Apache configuration required for local development.
After installing on a server
- Install a layout from
lazysite-layouts or write your own
layout.tt - Install a compatible theme (manager UI or zip upload) and set
layout:andtheme:inlazysite.conf - Edit
public_html/lazysite/nav.confto define your site navigation - Edit
public_html/index.mdfor your home page content - Add pages by dropping
.mdfiles anywhere in the docroot
Pages are available immediately at their extensionless URL:
public_html/about.md -> https://example.com/about
public_html/services/hosting.md -> https://example.com/services/hosting
public_html/services/index.md -> https://example.com/services/
Directory index pages are served when a trailing slash URL is requested.
Create dirname/index.md for any directory that needs an index page.
Using an AI assistant
Four audience-specific briefings live under starter/docs/:
ai-briefing-authoring.md- writing contentai-briefing-layouts.md- designing layouts and themesai-briefing-configuration.md- configuring a siteai-briefing-development.md- working on the codebase
Feed the relevant briefing to an AI assistant at the start of a session to enable help without needing to explain the system each time. In Claude Projects, save it as a project document. For other AI tools, paste it as context at the start of the conversation.
Upgrading
Install the newer lazysite on the host, then move each site to it:
lazysite upgrade --docroot /path/to/public_html
Seed files you have edited (starter pages, docs) are preserved; code files (processor, extensions, manager UI) are always refreshed. Run it as the site's user — it refuses root.
Inspect the plan before committing to it:
lazysite upgrade --docroot /path/to/public_html --dry-run
--dry-run reports what would change and writes nothing at all. It is a
single-site preview and does not combine with --all.
Before applying an upgrade — or a reinstall — lazysite writes a backup tarball
to {docroot}/lazysite/backups/. backup_retention in lazysite.conf
controls how many are kept (default 3; 0 = keep all).
A whole host at once
sudo lazysite upgrade --all
As root this drops to each site's own user, so no site tree is written as root.
Sites whose update_policy is manual (the default) are skipped unless
--force is given, and each site's update_channel is then honoured — see
Update channel.
After an upgrade, restart anything that holds engine code
An ordinary CGI site reads the engine fresh on every request and needs nothing.
A site running the persistent runtime or the FastCGI pool has the previous
engine in memory and goes on serving it until the worker restarts. upgrade
and reinstall name the units to restart when there are any; upgrade --all,
which runs as root, restarts them itself.
If something is wrong and the version is not changing
Two different problems, two different commands:
| Symptom | Command |
|---|---|
| engine files were edited or lost; put the shipped ones back | lazysite reinstall --docroot D |
| the files are right but ownership, modes or missing directories are not | lazysite repair --docroot D |
repair changes no files; reinstall replaces them and leaves content,
accounts and configuration alone.
If an upgrade goes wrong
List the backups:
lazysite backups --docroot /path/to/public_html
Put the most recent one back:
lazysite backups --docroot /path/to/public_html --restore
Or a specific one:
lazysite backups --docroot /path/to/public_html --restore \
--backup /path/to/backup.tar.gz
Restoring does not touch runtime state (auth users, cache, logs) and invalidates the rendered HTML cache afterwards.
Uninstall
There is no uninstall script. Removal is manual, and deliberately so - the
installer only ever adds files to a domain's cgi-bin/ and the Hestia template
directory, so there is nothing to unwind beyond deleting them.
To remove lazysite from one domain, delete the engine scripts the installer
placed in that domain's cgi-bin/ (lazysite-processor.pl, lazysite-auth.pl
and the other lazysite-*.pl entry points) and restore the web-server
configuration that was in place before.
A domain's content is not touched by any of this. The public_html tree -
pages, lazysite/, uploads - is yours and stays exactly where it is. Back it up
before removing anything if you intend to move it elsewhere.
File reference
public_html/
lazysite/
lazysite.conf <- site configuration
nav.conf <- navigation (YAML)
layouts/
LAYOUT/
layout.tt <- layout template
layout.json <- layout metadata (optional)
themes/
THEME/
theme.json <- theme manifest
main.css <- theme stylesheet
assets/ <- theme assets
manager/ <- manager chrome (internal; D013)
layout.tt
assets/manager.css
templates/
registries/
llms.txt.tt
sitemap.xml.tt
feed.rss.tt
feed.atom.tt
auth/
users <- built-in auth users
groups <- built-in auth groups
forms/
handlers.conf <- the handlers a form or the schedule calls
schedule.conf <- what the timer calls
smtp.conf <- SMTP connection settings
assets/
css/
img/
js/
cgi-bin/
lazysite-processor.pl
lazysite-auth.pl
plugins/form-handler.pl
plugins/form-smtp.pl
lazysite-manager-api.pl
404.md
index.md
Further reading
- Authoring - Markdown, front matter, TT variables
- Configuration - views, nav.conf, lazysite.conf, themes
- Reference - front matter keys, variables, file locations
- Development - dev server, troubleshooting, build tools
- API mode - raw mode, JSON endpoints, query strings
Licence
MIT
AI assistance
lazysite was developed interactively with Claude (Anthropic). Architecture, design decisions, security review, and deployment were directed by the author. Claude assisted with code generation, documentation, and iterative refinement throughout development.