Documentation · Get started
Your first site
Install Ehundu, create a site with a menu and a blog, see it in the browser and publish it.
Version 0.16
In this walkthrough you install Ehundu and build a small site with a home page, another page, a menu and a blog. It is just enough to understand how a project is thought out. It will take you about twenty minutes.
One thing first: the format of an Ehundu project is written in Spanish on purpose, so folders and fields have Spanish names (contenido/, plantillas/, titulo, fecha…). Each one is explained the first time it appears, and the quick reference has all of them in one table.
Before you start¶
You need PHP 8.4 or later with the mbstring extension. Check your version:
php --version
If it says 8.4 or higher, and php -m includes mbstring, you can go on.
1. Install Ehundu¶
Ehundu is a single file, ehundu.phar. Download it from the latest release and put it wherever you like:
curl -L -o ehundu.phar https://github.com/ehundu/ehundu/releases/latest/download/ehundu.phar
php ehundu.phar --version
It should print Ehundu and the version number. On Windows, instead of curl you can use the browser or Invoke-WebRequest. If you prefer Composer, composer require ehundu/ehundu installs it and the command is vendor/bin/ehundu.
In what follows I write php ehundu.phar. If you installed it with Composer, change it to vendor/bin/ehundu.
2. Create the project¶
A project is a folder. Create one and go into it:
mkdir my-site
cd my-site
The only required file is sitio.yml (sitio is “site”), with the name of the site and the address of the domain where it will live, without a path. nombre is the name and url is the address:
nombre: My first site
url: https://example.com
3. Write a page and its template¶
Pages live in the contenido/ folder (“content”). They are written in Markdown, with some data at the top between two lines of dashes (it is called “front matter”). Create contenido/index.md; titulo is the title:
---
titulo: Hello, world
---
This is my first page with Ehundu.
Every page needs a template that wraps it in HTML. They are written in Twig, and the one used by default is plantillas/pagina.twig (“templates / page”). Create that file:
<!doctype html>
<html lang="{{ pagina.idioma }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ pagina.titulo }} · {{ sitio.nombre }}</title>
</head>
<body>
<h1>{{ pagina.titulo }}</h1>
{{ pagina.contenido }}
</body>
</html>
pagina (“page”) is the page being built: its title is there, its text already converted to HTML (pagina.contenido) and any other data from the front matter. sitio (“site”) is what you wrote in sitio.yml.
4. See it¶
php ehundu.phar servir
Ehundu builds the site and tells you where to see it: http://127.0.0.1:8000/. Open it in the browser. Leave the command running: every time you save a change it rebuilds only what is needed, and reloading the page is enough. Ctrl+C stops it.
When you want the finished site, build it:
php ehundu.phar compilar
A salida/ folder (“output”) appears with plain, ordinary HTML, ready to upload to any server.
5. Another page and a menu¶
Every file in contenido/ is a page, and its address comes from its name. Create contenido/about-me.md, which will be /about-me/:
---
titulo: About me
---
My name is Ana and this is **my website**.
To make it reachable, a menu. Data that is repeated on several pages goes in the datos/ folder (“data”), one file each. Create datos/menu.yml:
- texto: Home
enlace: /
- texto: About me
enlace: /about-me/
- texto: Blog
enlace: /blog/
And the pieces of HTML you reuse go in parciales/ (“partials”). Create parciales/menu.twig:
<nav>
<ul>
{% for entrada in datos.menu %}
<li><a href="{{ entrada.enlace }}"{% if activo(entrada.enlace) %} aria-current="page"{% endif %}>{{ entrada.texto }}</a></li>
{% endfor %}
</ul>
</nav>
datos.menu is the content of datos/menu.yml, and activo() (“active”) tells you whether you are on that page. The partial is still missing from the templates. So as not to repeat the structure in each one, move what is common to plantillas/base.twig:
<!doctype html>
<html lang="{{ pagina.idioma }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ pagina.titulo }} · {{ sitio.nombre }}</title>
</head>
<body>
{% include 'parciales/menu.twig' %}
<main>
{% block cuerpo %}{% endblock %}
</main>
</body>
</html>
And leave plantillas/pagina.twig with only what belongs to a page:
{% extends 'plantillas/base.twig' %}
{% block cuerpo %}
<h1>{{ pagina.titulo }}</h1>
{{ pagina.contenido }}
{% endblock %}
With the preview running, reload: both pages now have a menu.
6. A blog¶
Create a folder contenido/blog/ with a _datos.yml file. Everything you put there is inherited by the pages in the folder:
etiquetas: [blog]
url: "/blog/{{ titulo|slug }}/"
This way, every page in blog/ belongs to the blog collection (etiquetas means “tags”), and its address comes from its title (“My first post” becomes /blog/my-first-post/). Write two posts, each with a date. For example, contenido/blog/first-post.md:
---
titulo: My first post
fecha: 2026-10-15
---
Today I start a blog.
And contenido/blog/another-one.md, with another title and another date. Now the page that lists them, contenido/blog/index.md. It has its own address and uses another template, lista (“list”):
---
titulo: Blog
plantilla: lista
url: /blog/
---
And the template plantillas/lista.twig loops over the collection:
{% extends 'plantillas/base.twig' %}
{% block cuerpo %}
<h1>{{ pagina.titulo }}</h1>
<ul>
{% for entrada in coleccion('blog')|sin(pagina)|orden('fecha desc') %}
<li><a href="{{ entrada.url }}">{{ entrada.titulo }}</a>, {{ entrada.fecha|fecha }}</li>
{% endfor %}
</ul>
{% endblock %}
coleccion('blog') (“collection”) is the pages with that tag. sin(pagina) (“without”) removes the list page itself, which, being in the folder, carries the tag too. orden('fecha desc') (“order”) puts the most recent first, and fecha writes the day in the language of the page: “20 October 2026”.
7. Publish it¶
Tell Ehundu where to upload the site. Add the despliegue section (“deployment”) to sitio.yml (here, over SFTP):
despliegue:
destino: sftp
servidor: sftp.example.com
usuario: ana
ruta: /home/ana/www
The password never goes in sitio.yml, which is shared and versioned. It goes in a .secretos.yml file (“secrets”), at the root of the project, in single quotes:
despliegue:
clave: 'the-password'
Before uploading anything, see what it would do:
php ehundu.phar desplegar --simular
The first time, with SFTP, it stops and gives you the server's fingerprint so that you can check it with whoever administers it and write it down in sitio.yml (huella:). When everything matches:
php ehundu.phar desplegar
It builds the site and uploads only what is new or has changed. If you use Git, do not version salida/, .ehundu/ or .secretos.yml. You can also publish to a folder, over FTP or to an S3 bucket: change the destino (“destination”).
And now what?¶
- The guides explain one idea each: the structure of a project, templates and shortcodes, several languages and how to deploy.
- The quick reference has at hand the fields, functions and shortcodes you will use every day.
- To see everything a project supports, there is the format (in Spanish), the complete reference document.
- If something does not go as you expected, Ehundu warns you with the file and the line. Read it slowly: it almost always says what to do.