Tutorial completo: Montar tu blog writefreely federado en tu vps
Por Calvo Barba Blanca | 11 Octubre 2026
Tabla de Contenidos
- Contexto y Decisión
- Requisitos Previos
- Preparación del DNS
- Conexión al VPS
- Estructura de Directorios
- docker-compose.yml
- Asistente de Configuración
- Permisos y Claves
- Entrypoint vs Command
- Integración con Caddy
- Verificación Final
- Errores y Soluciones
- Recursos y Enlaces
1. Contexto y Decisión
Este tutorial documenta la instalación de WriteFreely en mi VPS de OVH, autoalojada, bajo mi dominio https://blog.calvobarbablanca.es. La decisión vino de la mano del cierre de Listed (mi plataforma anterior) el 31 de diciembre de 2026. Lanzé una encuesta en Mastodon y mi comunidad eligió WriteFreely sobre Mataroa (13 votos), apostando por el autoalojamiento en mi propio servidor.
Lo que obtendrás al final:
Un blog federado con ActivityPub Accesible desde cualquier instancia del Fediverso SSL automático con Let's Encrypt SQLite como base de datos (ligero) Total control sobre tus datos
Lo que no encontrarás aquí:
Una instalación “perfecta de libro” — esta es la instalación real, con todos los errores incluidos. Aquí lo que tienes es el paso a paso estructurado para cuando quieras seguirlo a ritmo propio.
2. Requisitos previos
Lo que necesitas antes de empezar – VPS Ubuntu 24.04 LTS (o similar) – Dominio Con acceso a gestión DNS (Nominalia, Gandi, etc.) – Acceso SSH Puerto 22 abierto, usuario con sudo – Docker Instalado previamente – Proxy inverso Caddy o Nginx (yo uso Caddy standalone) – Puerto libre 8083 (o el que elijas para WriteFreely)
Mi entorno específico (para referencia) – VPS OVH: 4 vCore, 8GB RAM, 75GB NVMe – IP pública: 51.210.15.247 – Soporte: Ubuntu 24.04.4 LTS – Coste mensual: 5,83€ – Dominio registrado en: Nominalia
3. Preparación del DNS
⚠️ Haz esto PRIMERO — el DNS tarda 5-30 minutos en propagarse. Mientras se propaga, puedes ir instalando el resto.
3.1 Crear el registro DNS
Ve al panel de gestión de tu dominio → Zona DNS → Añadir nuevo registro:
- Tipo A
- Nombre/Host blog
- Destino/Valor 51.210.15.247 (tu IP del VPS)
- TTL 300
3.2 Verificar propagación
Desde tu máquina local:
dig +short blog.tudominio.es
# Debería devolver tu ip pública
Si no devuelve nada, espera 5 minutos e intenta de nuevo. Puedes seguir propagando en paralelo mientras montas el contenedor.
4. Conexión al VPS
4.1 Conectar por SSH
ssh usuario@ip pública
4.2 ¡SIEMPRE con tmux!
tmux new -s writefreely
Por qué tmux: Si se corta la conexión (interrupción eléctrica, mala señal, etc.), el proceso sigue corriendo dentro del tmux. Sin tmux, si cae la sesión, puedes dejar el contenedor en estado inconsistente. Es una lección que aprendí con Castopod.
4.3 Comprobaciones previas
Verifica que los servicios base están arriba:
sudo docker ps
En mi caso, tenía activos:
- nextcloud-app-1
- castopod-app
- mariadb
- postgres
Y el puerto 8083 debe estar libre:
sudo ss -tlnp | grep :8083
# NO debe devolver nada
5. Estructura de Directorios
mkdir -p ~/writefreely/{config,data,keys}
cd ~/writefreely
Esto crea tres carpetas separadas para facilitar backups y mantenimiento:
- config/ → configuración (config.ini)
- data/ → base de datos SQLite
- keys/ → claves criptográficas (CRÍTICO — nunca borrar)
5.1 Verifica la creación
ls -la
# Debes ver: config data keys
6. docker-compose.yml
Crea el archivo:
nano docker-compose.yml
Contenido exacto:
services:
writefreely:
image: ghcr.io/writefreely/writefreely:latest
container_name: writefreely
restart: unless-stopped
command: /go/cmd/writefreely/writefreely -c /config/config.ini
ports:
- "8083:8080"
volumes:
- ./config:/config
- ./data:/data
- ./keys:/go/keys
6.1 Detalles importantes
- Imágenes: ghcr.io/writefreely/writefreely:latest (registro oficial de GitHub)
- Mapeo de puerto: 8083:8080 — dentro del contenedor escucha en 8080, en el host usamos 8083
- Volúmenes: los tres son persistentes — sobreviven a reinicios del contenedor Guarda con Ctrl+O, Enter, Ctrl+X.
7. Asistente de Configuración
7.1 Lanzar el contenedor
sudo docker compose up -d
7.2 Ejecutar el asistente interactivo
IMPORTANTE: El binario de WriteFreely NO está en el PATH por defecto. La ruta completa es /go/cmd/writefreely/writefreely:
sudo docker compose run --rm --entrypoint /go/cmd/writefreely/writefreely writefreely -c /config/config.ini config start
7.3 Respuestas del asistente
El asistente te hará preguntas interactivamente. Estas fueron las mías:
- Environment production
- Site name Bitácora del Calvo Barba Blanca
- Single-user or multi-user? single user
- Site's root URL / host https://blog.calvobarbablanca.es
- Port (interior) 8080
- Database type sqlite3
- Database filename /data/writefreely.db
- Allow anyone to sign up? false
- Federation (ActivityPub) true
7.4 Posible error en este paso
Si recibes permission denied al escribir el config.ini, ve al paso 8 antes de continuar. Los volúmenes necesitan permisos abiertos durante la instalación.
8. Permisos y Claves
8.1 Problema detectado
El contenedor corre como usuario bin (uid 2), pero nuestras carpetas son de usuario. Necesitamos dar acceso de escritura:
sudo chmod 777 ~/writefreely/config ~/writefreely/data ~/writefreely/keys
8.2 Generar claves criptográficas
sudo docker compose run --rm --entrypoint /go/cmd/writefreely/writefreely writefreely -c /config/config.ini keys generate
8.3 ⚠️ Problema común: claves anidadas
WriteFreely crea una subcarpeta keys/ dentro de keysparentdir. Si tu config dice keysparentdir = /go/keys, las claves acabarán en /keys/keys/ en lugar de /keys/.
Verifica:
ls -la ~/writefreely/keys/
Si ves una carpeta keys/ dentro, mueve los archivos afuera:
sudo mv ~/writefreely/keys/keys/* ~/writefreely/keys/
sudo rmdir ~/writefreely/keys/keys
Solución en config.ini: Cambia keysparentdir a /go (sin el /keys):
keys_parent_dir = /go
8.4 Inicializar base de datos
sudo docker compose run --rm --entrypoint /go/cmd/writefreely/writefreely writefreely -c /config/config.ini db init
8.5 Reiniciar el servicio
sudo docker compose down
sudo docker compose up -d
8.6 Verificar estado
sudo docker ps | grep writefreely
Deberías ver: writefreely Up (NO Restarting)
9. Entrypoint vs Command
9.1 El problema del bucle infinito
Si el contenedor sigue reiniciándose (Restarting (1)), mira los logs:
sudo docker logs --tail 30 writefreely
Si dice No configuration file yet aunque el config.ini existe, el problema es el conflicto entre ENTRYPOINT y CMD.
9.2 La solución correcta
La imagen oficial de WriteFreely ya tiene un ENTRYPOINT propio. Si defines command: en el compose, se añade después del entrypoint, generando una línea mal formada.
Dos soluciones posibles:
Opción A (la que usé): Sobrescribir entrypoint: con ruta completa:
services:
writefreely:
entrypoint: ["/go/cmd/writefreely/writefreely", "-c", "/config/config.ini"]
Opción B (más limpia): Dejar el entrypoint y asegurar que el comando sea compatible:
services:
writefreely:
command: writefreely -c /config/config.ini
# Pero el binario debe estar en PATH (no siempre lo está)
Yo usé la Opción A porque funcionó a la primera.
9.3 Verificación
curl -I http://127.x.x.1:8083
Debería devolver HTTP/1.1 200 OK (o 303).
10. Integración con Caddy
10.1 Configurar el proxy inverso
Edita tu Caddyfile:
sudo nano /etc/caddy/Caddyfile
Añade el bloque:
# blog.tudominio.es → WriteFreely (puerto 8083)
blog.calvobarbablanca.es {
reverse_proxy http://127.0.0.1:8083
encode gzip zstd
}
10.2 Validar y recargar
sudo caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
sudo caddy fmt --overwrite /etc/caddy/Caddyfile
sudo systemctl reload caddy
sudo journalctl -u caddy -n 20 --no-pager
En los logs deberías ver Caddy obteniendo el certificado Let's Encrypt para tu blog en mi caso blog.calvobarbablanca.es.
11. Verificación Final
11.1 Pruebas desde la terminal
# DNS
dig +short blog.calvobarbablanca.es
# → Debe devolver: 51.210.15.247
# HTTPS local
curl -I http://127.0.0.1:8083
# → HTTP/1.1 200 OK o 303
# HTTPS completo
curl -I https://blog.calvobarbablanca.es
# → HTTP/2 200
11.2 Acceso web
Abre en tu navegador: https://blog.calvobarbablanca.es
11.3 Usuario admin Si no recuerdas la contraseña del administrador:
# Crear usuario admin (si no existe)
sudo docker compose exec writefreely /go/cmd/writefreely/writefreely -c /config/config.ini user create --admin calvo:NuevaPassTemporal2026
# O resetear contraseña de usuario existente
sudo docker compose exec writefreely /go/cmd/writefreely/writefreely -c /config/config.ini user reset-pass usuario
11.4 Probar la federación
Desde tu cuenta Mastodon: busca @usuario@blog.tudominio.es y síguelo. Tus posts llegarán al timeline federado de tus seguidores.
12. Errores y Soluciones
Esta sección resume los fallos reales de esta instalación. Son los que más importan, porque los libros no los cuentan.
- permission denied al escribir config.ini Carpeta config/ sin permisos para uid 2 chmod 777 ~/writefreely/config
- Contenedor en bucle Restarting (1) Conflictos ENTRYPOINT/CMD Usar entrypoint: con ruta completa en compose
- 404 en la raíz del blog Usuario admin no creado aún user create o user reset-pass
- attempt to write a readonly database Carpeta data/ cerrada para uid 2 chmod 777 ~/writefreely/data
- Unable to load encryption keys Carpeta keys/ sin permisos de lectura chmod 777 ~/writefreely/keys
- Claves en /keys/keys/ (anidadas) keysparentdir = /go/keys Cambiar a keysparentdir = /go
- Binario “executable not found” PATH del contenedor no incluye WriteFreely Usar ruta completa: /go/cmd/writefreely/writefreely
No configuration file yet config.ini existe pero el binario no lo encuentra Verificar mount del volumen y path en command/entrypoint
13. Recursos y Enlaces
13.1 Herramientas usadas
WriteFreely Blog minimalista federado
Docker Contenedorización
Caddy Proxy inverso + SSL automático
tmux Sesión persistente SSH
SQLite Base de datos ligera
13.2 Enlaces oficiales
WriteFreely Docker: https://writefreely.org/docs/main/admin/docker
WriteFreely GitHub: https://github.com/writefreely/writefreely
Caddy: https://caddyserver.com
OVHcloud: https://ovhcloud.com
13.3 Mis otros servicios autoalojados
Nextcloud minimal: https://tareas.calvobarbablanca.com
Castopod: https://podcast.calvobarbablanca.es
Owncast: https://directo.calvobarbablanca.es
13.4 Sobre mí
Soy Calvo Barba Blanca, padre, gamer y defensor de la soberanía digital. Uso software libre desde 2005. En este blog escribo reflexiones y aprendizajes sobre privacidad, Linux y el Fediverso. Todo autoalojado, todo cifrado, todo bajo mi control.
Si quieres ver el proceso completo en vídeo:
Contacto y Federacion
Puedes encontrarme en el Fediverso en: Mastodon: https://tuiter.rocks/@wildperal WriteFreely: @calvo@blog.calvobarbablanca.es Este post está federado.
Última actualización: 11 Octubre 2026 Tecnología: WriteFreely + Docker + Caddy Licencia: Contenido bajo CC BY-SA 4.0