Post

Bibliobox (biblioteca digital autónoma)

Bibliobox (biblioteca digital autónoma)

Fugas tecno-lógicas

Bibliobox.isla

bibliobox

última actualización 2026.08.20

Conocimientos previos

No es necesario ser especialista en Linux, Docker o desarrollo web. Es recomendable tener cierta familiaridad con:

  • Uso básico de una terminal
  • Navegación por directorios (cd, ls, cp.)
  • Copiar y mover archivos
  • Uso básico de Git
  • Conceptos básicos de redes
  • Qué es una dirección IP
  • Qué es una red Wi-Fi

Requisitos técnicos:

Computadora

  • Linux instalado (Fedora, Debian, Ubuntu, Linux Mint, Arch, etc.)
  • 4 GB de RAM como mínimo
  • 10 GB de espacio libre en disco
  • Al menos un puerto USB disponible
  • Conexión a Internet
  • Conexión Wi-Fi
  • lector de tarjetas SD (integrado o por usb)

#1 Crear nuestra Rasberry Pi OS en la microSD:

  1. Con lsblk -f encontramos el nombre de la memoria microSD comunmente llamada: of=/dev/mmcblk0`

  2. Luego dd podemos volcar la imagen en la memoria microSD del sistema operativo Rasperry Pi OS:

xzcat 2026-04-21-raspilsos-trixie-arm64-lite.img.xz | sudo dd of=/dev/mmcblk0 bs=4M status=progress conv=fsync

  1. Montar la partición boot > hacer antes del primer inicio (IMPORTANTE)
  • primero vemos que particiones tenemos y con qué nombres

lsblk -f

  • luego montamos boot y generamos usuarie y contraseña ej: bibliobox:bibliobox

echo "bibliobox:$(openssl passwd -6 bibliobox)" > userconf

  • chequeamos que este creado

cat userconf

touch ssh

  1. Conectamos la alimentación y el cable de red a la raspi junto con un router.

  2. Buscar la raspi en el router o escanear la red

1
2
3
4
5
6
7
8
9
10
11
12
sudo arp-scan 192.168.1.0/24 (ip del portal del router)

Interface: wlp3s0, type: EN10MB, MAC: 7e:6f:05:a7:ed:80, IPv4: 192.168.1.49
Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan)
192.168.1.1	2c:96:82:2b:58:44	(Unknown)
192.168.1.34	ec:2e:98:da:ee:b3	AzureWave Technology Inc.
192.168.1.48	a8:64:f1:f8:e2:1a	Intel Corporate
192.168.1.50	ba:99:77:e5:86:0e	(Unknown: locally administered)
192.168.1.51	e0:99:71:af:0e:4d	Samsung Electronics Co.,Ltd
192.168.1.41	36:0e:52:19:9a:7a	(Unknown: locally administered)
192.168.1.37	94:db:c9:f7:2b:ae	AzureWave Technology Inc.
192.168.1.64	40:e2:30:f7:bd:15	AzureWave Technology Inc.
  1. Detectar en nuestro router que ip nos asigno para la rpi y entrar con ssh

por ej: ssh bibliobox@192.168.1.103

#2 Instalar bibliobox-script

La Raspberry Pi crea una red WiFi local y publica una biblioteca digital mediante un sitio web estático. También es accesible a través de un router si se lo conecta por eth0 con el nombre del http://$SSID.local.

Instalar dependencias para usar el repositorio bibliobox script

sudo apt update

sudo apt install git

  1. clonar el repositorio dentro de la RPi en el ~/home

git clone https://gitlab.com/librenauta/bibliobox-script.git

Configuración

Editar únicamente config.sh.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Bibliobox
SSID="Bibliobox"
DOMAIN="bibliobox.isla"
SITE_PATH="/srv/bibliobox.isla"

# Red
INTERFACE="wlan0"
IP="192.168.4.1"
SUBNET="24"

# DHCP
DHCP_RANGE_START="192.168.4.10"
DHCP_RANGE_END="192.168.4.100"

# WiFi | AR=argentina | México=MX
WIFI_COUNTRY="AR"
WIFI_CHANNEL="6"

# Portal
MAX_CLIENTS="100"

SSID es el nombre de la red de Wifi y también el nombre del dominio .local

DOMAIN y SITE_PATH son independientes pero están relacionados:

  • DOMAIN es el nombre que puede escribir una persona en el navegador.
  • SITE_PATH es el directorio donde Nginx busca la biblioteca.

Por ejemplo:

1
2
3
4
5
http://bibliobox.isla
        ↓
192.168.4.1
        ↓
/srv/bibliobox.isla

Instalación completa

Desde este directorio:

1
2
# usamos sudo bash asi no tenemos que asignarle permisos de ejecución a cada script
sudo bash install.sh

Instalación por etapas

Cada etapa puede ejecutarse por separado:

1
2
3
4
5
6
sudo bash ./01-paquetes.sh
sudo bash ./02-red.sh
sudo bash ./03-wifi.sh
sudo bash ./04-portal.sh
sudo bash ./05-sitio.sh
sudo bash ./06-avahi.sh

Cada script carga automáticamente la configuración desde config.sh.

1
2
3
4
5
6
7
┌──────────────┐     ┌──────────────────┐     ┌──────────────────┐     ┌────────────────────┐
│   CELULAR    │     │     hostapd      │     │     dnsmasq      │     │       nginx        │
│              │───▶│    Bibliobox     │───▶│                  │───▶│                    │
│  conecta a   │ WiFi│                  │     │  bibliobox.isla  │     │  root              │
│     WiFi     │     │                  │     │        ↓         │     │ /srv/bibliobox.isla│
└──────────────┘     └──────────────────┘     │    192.168.4.1   │     └────────────────────┘
                                              └──────────────────┘
  1. Paso a paso de las etapas del script

install.sh

Este script llama a cada etapa en forma de cascada y toma como fuente de variables globales lo que definimos en config.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/bin/bash

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/config.sh"

echo "[install] Iniciando instalación de Bibliobox"
echo "[install] WiFi: $SSID"
echo "[install] Dominio: $DOMAIN"
echo "[install] Sitio: $SITE_PATH"
echo "[install] URL para acceder si conectamos un router a la RPI http://$MDNS_HOSTNAME.local"

bash "$SCRIPT_DIR/01-paquetes.sh"
bash "$SCRIPT_DIR/02-red.sh"
bash "$SCRIPT_DIR/03-wifi.sh"
bash "$SCRIPT_DIR/04-portal.sh"
bash "$SCRIPT_DIR/05-sitio.sh"
bash "$SCRIPT_DIR/06-avahi.sh"

echo "[install] Habilitando servicios"

systemctl unmask hostapd

systemctl enable \
    hostapd \
    dnsmasq \
    nginx \
    nodogsplash

echo "[install] Reiniciando servicios"

systemctl restart dhcpcd || true
systemctl restart dnsmasq
systemctl restart hostapd
systemctl restart nginx
systemctl restart nodogsplash

echo "[install] Instalación completa ∩`-´)⊃━☆゚.*・。゚*.-"
echo "[install] WiFi: $SSID"
echo "[install] Portal: http://$DOMAIN/"
echo "[install] IP: http://$IP/"
echo "[install] Ubicación del sitio: $SITE_PATH"
echo "[install] URL .local: http://$MDNS_HOSTNAME.local"

01-paquetes.sh

Instalamos los paquetes necesarios para nuestra instalación.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/config.sh"

log() {
    echo "[Ejecutando 01-paquetes] $1"
}

log "Instalando paquetes"

apt update

apt install -y \
    hostapd \
    dnsmasq \
    nginx \
    rfkill \
    git \
    build-essential \
    libmicrohttpd-dev \
    libjson-c-dev \
    dhcpcd5

02-red.sh

configuramos una ip estática y configuramos dnsmasq que permite generar un dns local así podemos usar nuestro dominio personalizado (sin https).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#!/bin/bash

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/config.sh"

log() {
    echo "[Ejecutando 02-red] $1"
}

log "Deteniendo servicios"

systemctl stop hostapd dnsmasq nginx nodogsplash 2>/dev/null || true

log "Desactivando wpa_supplicant"

systemctl disable wpa_supplicant 2>/dev/null || true
systemctl stop wpa_supplicant 2>/dev/null || true

log "Configurando IP estática"

if ! grep -q "$IP" /etc/dhcpcd.conf 2>/dev/null; then
    cat <<EOF >> /etc/dhcpcd.conf

interface $INTERFACE
static ip_address=$IP/$SUBNET
nohook wpa_supplicant
EOF
fi

log "Activando IP forwarding"

if ! grep -q "net.ipv4.ip_forward=1" /etc/sysctl.conf; then
    echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
    sysctl -p
fi

log "Configurando dnsmasq"

if [ ! -f /etc/dnsmasq.conf.bak ]; then
    mv /etc/dnsmasq.conf /etc/dnsmasq.conf.bak 2>/dev/null || true

    cat <<EOF > /etc/dnsmasq.conf
interface=$INTERFACE
dhcp-range=$DHCP_RANGE_START,$DHCP_RANGE_END,255.255.255.0,12h
address=/#/$IP
EOF
fi

03-wifi.sh

Configuramos hostapd para generar el hotspot (Generar red de Wifi), creamos el servicio en systemd para que inice al reiniciar.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#!/bin/bash

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/config.sh"

log() {
    echo "[Ejecutando 03-wifi] $1"
}

log "Desbloqueando WiFi"

if rfkill list wifi | grep -q "Soft blocked: yes"; then
    rfkill unblock wifi
fi

log "Configurando país WiFi: $WIFI_COUNTRY"

iw reg set "$WIFI_COUNTRY" || true

log "Configurando hostapd"

cat <<EOF > /etc/hostapd/hostapd.conf
interface=$INTERFACE
driver=nl80211
ssid=$SSID
hw_mode=g
channel=$WIFI_CHANNEL
auth_algs=1
ignore_broadcast_ssid=0
EOF

sed -i \
    's|^#DAEMON_CONF=.*|DAEMON_CONF="/etc/hostapd/hostapd.conf"|' \
    /etc/default/hostapd 2>/dev/null || \
echo 'DAEMON_CONF="/etc/hostapd/hostapd.conf"' >> /etc/default/hostapd

log "Creando servicio wlan0-up"

cat <<EOF > /etc/systemd/system/wlan0-up.service
[Unit]
Description=Levantar wlan0
Before=hostapd.service

[Service]
Type=oneshot
ExecStart=/sbin/ip link set $INTERFACE up

[Install]
WantedBy=multi-user.target
EOF

systemctl enable wlan0-up

log "Creando servicio rfkill-unblock"

cat <<EOF > /etc/systemd/system/rfkill-unblock.service
[Unit]
Description=Unblock WiFi
Before=hostapd.service

[Service]
Type=oneshot
ExecStart=/bin/sh -c "sleep 2 && rfkill unblock wifi"

[Install]
WantedBy=multi-user.target
EOF

systemctl enable rfkill-unblock

04-portal.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#!/bin/bash

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/config.sh"

log() {
    echo "[Ejecutando 04-portal] $1"
}

log "Configurando nginx"

rm -f /etc/nginx/sites-enabled/default

cat <<EOF > /etc/nginx/sites-available/bibliobox
server {
    listen 80 default_server;
    server_name $DOMAIN bibliobox.local _;
    root $SITE_PATH;
    index index.html;

    location / {
        try_files \$uri /index.html;
    }
}
EOF

ln -sf /etc/nginx/sites-available/bibliobox \
    /etc/nginx/sites-enabled/bibliobox

log "Instalando Nodogsplash"

if ! command -v nodogsplash >/dev/null; then
    cd /tmp
    rm -rf nodogsplash
    git clone https://github.com/nodogsplash/nodogsplash.git
    cd nodogsplash
    make
    make install
fi

log "Configurando servicio nodogsplash"

cat <<EOF > /etc/systemd/system/nodogsplash.service
[Unit]
Description=Nodogsplash Captive Portal
After=hostapd.service dnsmasq.service
Wants=hostapd.service dnsmasq.service

[Service]
ExecStart=/usr/local/bin/nodogsplash -f
Restart=always

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reexec

log "Configurando nodogsplash"

if [ ! -f /etc/nodogsplash/nodogsplash.conf ]; then
    mkdir -p /etc/nodogsplash

    cat <<EOF > /etc/nodogsplash/nodogsplash.conf
GatewayInterface $INTERFACE
GatewayAddress $IP
MaxClients $MAX_CLIENTS

SplashPage none
AuthenticateImmediately yes
RedirectURL http://$IP/

FirewallRuleSet authenticated-users {
    FirewallRule allow all
}

FirewallRuleSet preauthenticated-users {
    FirewallRule allow tcp port 80
    FirewallRule allow tcp port 443
    FirewallRule allow udp port 53
}
EOF
fi

05-sitio.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/bin/bash

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/config.sh"

log() {
    echo "[Ejecutando 05-sitio web] $1"
}

log "Preparando sitio"

mkdir -p "$SITE_PATH"

if [ ! -f "$SITE_PATH/index.html" ]; then

cat <<EOF > "$SITE_PATH/index.html"
<!DOCTYPE html>
<html lang="es">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>$DOMAIN</title>
</head>

<body style="margin:0;background:#000;color:#fff;font-family:monospace;display:flex;align-items:center;justify-content:center;min-height:100vh;text-align:center;padding:2rem;box-sizing:border-box;">

  <main>

    <h1 style="margin:0 0 1rem;font-size:clamp(20px,4vw,26px);font-weight:400;line-height:1.4;">
      Bibliobox creada en el CCD | CDMX
    </h1>

    <p style="margin:0;font-size:clamp(14px,2.5vw,16px);line-height:1.6;">
      reemplaza este sitio por tu biblioteca
    </p>

  </main>

</body>
</html>
EOF

fi

06-avahi.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash
set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/config.sh"

log() { echo "[Ejecutando 06-avahi] $1"; }

log "SSID original: '$SSID' -> Nombre de host mDNS: '$MDNS_HOSTNAME'"

log "Instalando avahi-daemon y herramientas"
apt install -y avahi-daemon avahi-utils

log "Configurando nombre de host a '$MDNS_HOSTNAME'"
CURRENT_HOSTNAME=$(hostname)
if [ "$CURRENT_HOSTNAME" != "$MDNS_HOSTNAME" ]; then
    hostnamectl set-hostname "$MDNS_HOSTNAME"
    # Actualizar /etc/hosts para que el sistema se reconozca a sí mismo
    sed -i "s/^127.0.1.1\s*.*/127.0.1.1\t$MDNS_HOSTNAME/" /etc/hosts
fi

log "Habilitando e iniciando avahi-daemon"
systemctl enable avahi-daemon
systemctl restart avahi-daemon

log "Avahi andando. La RPi se anunciará como $MDNS_HOSTNAME.local en todas las interfaces."

Con esto ejecutado tenemos nuestra Raspberry PI que inicia y genera una red de wifi sirviendo un sitio según el nombre que asignamos en config.sh

Equipo donde gestionamos la biblioteca.

  1. Instalar [Calibre] este será el lugar donde volcaremos los libros y los organizarémos por etiquetas, autoras, series.
  2. Volcar libros en una biblioteca.
  3. Editar metadata de los libros y asegurarnos que tienen todos portadas [tapas], en el buscador de calibre podemos poner: cover:false para filtrar los libros que no tengan una tapa, y podemos crearle una en Modificar metadatos > Generar portada

git clone https://gitlab.com/librenauta/bibliobox-docker.git

sudo docker pull registry.gitlab.com/librenauta/bibliobox-docker:latest

sudo docker compose run --rm bibliobox

  1. clonar el repositorio bibliobox-docker con:

git clone https://gitlab.com/librenauta/bibliobox-docker.git

cd bibliobox-docker/bibliobox-jekyll-theme

  1. Dentro de bibliobox-docker/bibliobox-jekyll-theme/books copiar con rsync nuestra biblioteca.

la carpeta donde guardamos nuestra biblioteca tiene que tener:

  • directorios con nombre de lxs autorxs
  • un archivo llamado metadata.db
  1. Con un programa que se llama rsync vamos a copiar nuestra carpeta ./books al directorio correspondiente en la Raspberry PI

rsync -avHAX --progress books/ bibliobox@bibliobox.isla:/usr/bibliobox/bibliobox-docker/bibliobox-jekyll-theme/books

  1. Dentro de bibliobox-jekyll-theme utilizar la imagen de docker con jekyll para compilar el sitio:

sudo docker compose pull

  1. Para compilar el sitio (crear la biblioteca sitio en base a los libros de la biblioteca de calibre)

sudo docker compose run --rm bibliobox

  1. Tenemos el sitio generado localmente en la RPi en bibliobox-jekyll-theme/_site

lo copiamos a nuestro directorio del servidor web para hacerlo disponible en la Red: sudo cp -r _site/. /srv/bibliobox.isla/

Ya tenemos disponible nuestra biblioteca-sitioweb con el dominio que asignamos en config.sh

A compartir!

This post is licensed under CC BY 4.0 by the author.