Páginas

miércoles, 18 de marzo de 2026

FileBrowser con Tailscale HTTPS Habilitado en FreeBSD 14.3

https://tailscale.com/docs/reference/examples/serve
https://tailscale.com/docs/how-to/quickstart
https://tailscale.com/docs/features/access-control
https://filebrowser.org/cli/filebrowser-config-init.html
https://filebrowser.org/cli/filebrowser-users-add.html

Instalar FileBrowser

pkg install filebrowser
which filebrowser
filebrowser version
File Browser v2.52.0/c11c986b7382a5c1f18d83ee7e6093dc0544cff9

Crear esttructura de directorios

Directorio para archivos compartidos

mkdir -p /home/carlos/fileserver

Directorio para configuracion

mkdir -p /home/carlos/.config/filebrowser

Iniciarlizar la base de datos de FileBrowser

filebrowser config init -d /home/carlos/.config/filebrowser/filebrowser.db

Crear usuario administrador

filebrowser users add carlos tu_contraseña_segura \
  --perm.admin \
  --perm.create \
  --perm.delete \
  --perm.download \
  --perm.execute \
  --perm.modify \
  --perm.rename \
  --perm.share \
  -d /home/carlos/.config/filebrowser/filebrowser.db

Explicación de permisos:

--perm.admin → Acceso administrativo completo
--perm.create → Crear archivos y carpetas
--perm.delete → Eliminar archivos
--perm.download → Descargar archivos
--perm.execute → Ejecutar comandos
--perm.modify → Modificar archivos
--perm.rename → Renombrar archivos/carpetas
--perm.share → Compartir enlaces públicos

Crear usuario adicional con permisos limitados

Detener completamente filebrowser

sudo service FileBrowser stop  
sudo pkill -9 filebrowser
filebrowser users add invitado password123 \
  --perm.download \
  --scope /home/carlos/fileserver/publico \
  -d /home/carlos/.config/filebrowser/filebrowser.db
 

Comprobar configuracion

 filebrowser config cat -d /home/carlos/.config/filebrowser/filebrowser.db
 

Ver usuarios

  
  filebrowser users ls -d /home/carlos/.config/filebrowser/filebrowser.db
  

Crear archivo /home/carlos/.config/filebrowser/config.json

  {
  "port": 8080,
  "baseURL": "",
  "address": "0.0.0.0",
  "log": "/home/carlos/.config/filebrowser/filebrowser.log",
  "database": "/home/carlos/.config/filebrowser/filebrowser.db",
  "root": "/home/carlos/fileserver"
}

Crear script de servicio rc.d sudo vim /usr/local/etc/rc.d/filebrowser

#!/bin/sh
# PROVIDE: filebrowser
# REQUIRE: DAEMON NETWORKING tailscaled
# KEYWORD: shutdown

. /etc/rc.subr

name="filebrowser"
rcvar="filebrowser_enable"

load_rc_config $name

: ${filebrowser_enable:="NO"}
: ${filebrowser_user:="carlos"}
: ${filebrowser_config:="/home/carlos/.config/filebrowser/config.json"}

pidfile="/home/carlos/.filebrowser.pid"
command="/usr/sbin/daemon"
filebrowser_command="/usr/local/bin/filebrowser"

start_cmd="${name}_start"
stop_cmd="${name}_stop"
status_cmd="${name}_status"

filebrowser_start()
{
    if [ -f ${pidfile} ] && kill -0 $(cat ${pidfile}) 2>/dev/null; then
        echo "${name} already running as pid $(cat ${pidfile})."
        return 1
    fi
    
    echo "Starting ${name}."
    /usr/bin/su -m ${filebrowser_user} -c "${filebrowser_command} -c ${filebrowser_config}" > /dev/null 2>&1 &
    echo $! > ${pidfile}
    chown ${filebrowser_user} ${pidfile}
}

filebrowser_stop()
{
    if [ -f ${pidfile} ]; then
        echo "Stopping ${name}."
        kill $(cat ${pidfile}) 2>/dev/null
        rm -f ${pidfile}
    else
        echo "${name} is not running."
    fi
}

filebrowser_status()
{
    if [ -f ${pidfile} ] && kill -0 $(cat ${pidfile}) 2>/dev/null; then
        echo "${name} is running as pid $(cat ${pidfile})."
    else
        echo "${name} is not running."
        return 1
    fi
}

run_rc_command "$1"

Dar permisos de ejecucion

sudo chmod +x /usr/local/etc/rc.d/filebrowser

Habilitar FileBrowser en rc.conf

sudo sysrc filebrowser_enable="YES"

Verificar Tailscale

tailscale status
100.69.99.114   tornado     nombreusuario@  freebsd  - 

Si no está corriendo

sudo service tailscaled start
tailscale up

Actualizar reglas de PF

# ========================================
# Configuración PF para tornado.local.com
# ========================================

# MACROS (Variables)
ext_if = "wlan0"                      # Interfaz de red
tailscale_if = "tailscale0"
local_net = "192.168.88.0/24"         # Red local permitida
ssh_port = "22"                       # Puerto SSH
filebrowser_port = "8080"
filebrowser_https_port = "8443"
http_port = "80"
https_port = "443"

# --- OPCIONES ---
set skip on lo0                       # No filtrar loopback
set block-policy drop                 # Descartar paquetes bloqueados silenciosamente
set loginterface $ext_if              # Interfaz para estadísticas

# --- SCRUB (Normalización de paquetes) ---
scrub in all                          # Normalizar todo el tráfico entrante

# --- POLÍTICA PREDETERMINADA ---
block all                             # Bloquear todo por defecto

# --- REGLAS ANTISPOOF ---
antispoof quick for $ext_if           # Protección contra IP spoofing

# --- PERMITIR LOOPBACK ---
pass quick on lo0 all                 # Permitir todo en loopback

# --- PERMITIR TRÁFICO EN TAILSCALE ---
pass quick on $tailscale_if all keep state

# --- Solo permite TAILSCALE ---
pass in quick on $tailscale_if all keep state

# --- PERMITIR ICMP (ping) desde red local ---
pass in quick on $ext_if inet proto icmp from $local_net to any icmp-type { echoreq } keep state

# --- PERMITIR SSH SOLO DESDE RED LOCAL ---
pass in quick on $ext_if proto tcp from $local_net to ($ext_if) port $ssh_port keep state

# --- PERMITIR FILEBROWSER HTTP DESDE RED LOCAL ---
pass in quick on $ext_if proto tcp from $local_net to ($ext_if) port $filebrowser_port keep state

# --- PERMITIR FILEBROWSER HTTP DESDE TAILSCALE ---
pass in quick on $tailscale_if proto tcp to any port $filebrowser_port keep state

# --- PERMITIR FILEBROWSER HTTPS DESDE RED LOCAL ---
pass in quick on $ext_if proto tcp from $local_net to ($ext_if) port $filebrowser_https_port keep state

# --- PERMITIR FILEBROWSER HTTPS DESDE TAILSCALE ---
pass in quick on $tailscale_if proto tcp to any port $filebrowser_https_port keep state

# --- PERMITIR TRÁFICO SALIENTE ---
pass out quick on $ext_if all keep state

# --- LOGGING (opcional) ---
# pass in log on $ext_if proto tcp from $local_net to ($ext_if) port $ssh_port keep state

Recargar reglas

sudo pfctl -f /etc/pf.conf
sudo service pf reload

Iniciar filebrowser

sudo service filebrowser start

Estado de filebrowser

sudo service filebrowser status

Puerto de escucha

sudo sockstat -4l | grep 8080
carlos   filebrowse  1322 5   tcp46  *:8080

Acceder a FileBrowser

Desde la red local:

http://192.168.88.183:8080

Desde Tailscale HTTPS (desde cualquier lugar):

https://100.69.99.114:443
https://tornado.tail0788a4.ts.net

Credenciales

Usuario carlos
Contraseña tu_contraseña_segura

Ver logs de diagnóstico

tail -f /home/carlos/.config/filebrowser/filebrowser.log

Proceso filebrowser

 ps aux | egrep filebrowser
root       1320   0.0  0.0   14376  2892 v0- I    Sun23   0:00.01 /usr/bin/su -m carlos \
-c /usr/local/bin/filebrowser -c /home/carlos/.config/filebrowser/config.json
carlos     1322   0.0  0.4 1279096 28916 v0- I    Sun23   0:25.27 /usr/local/bin/filebrowser \
-c /home/carlos/.config/filebrowser/config.json

Reglas PF activas

sudo pfctl -sr 
Password:
scrub in all fragment reassemble
block drop all
block drop in quick on ! wlan0 inet from 192.168.88.0/24 to any
block drop in quick inet from 192.168.88.183 to any
pass in quick on wlan0 inet proto tcp from 192.168.88.0/24 to \
(wlan0) port = ssh flags S/SA keep state
pass in quick on wlan0 inet proto tcp from 192.168.88.0/24 to \
(wlan0) port = http-alt flags S/SA keep state
pass in quick on wlan0 inet proto tcp from 192.168.88.0/24 to \
(wlan0) port = 8443 flags S/SA keep state
pass in quick on wlan0 inet proto icmp from 192.168.88.0/24 to \
any icmp-type echoreq keep state
pass out quick on wlan0 all flags S/SA keep state
pass quick on lo0 all flags S/SA keep state
pass quick on tailscale0 all flags S/SA keep state

IMPORTANTE: Detener FileBrowser antes de modificar la base de datos

sudo service filebrowser stop
sudo pkill -9 filebrowser

Cambiar contraseña de usuario

filebrowser users update carlos \
  --password nueva_contraseña \
  -d /home/carlos/.config/filebrowser/filebrowser.db

Habilitar branding personalizado

filebrowser config set \
  --branding.name "Servidor de Carlos" \
  --branding.disableExternal \
  -d /home/carlos/.config/filebrowser/filebrowser.db
  

Cambiar a tema oscuro por defecto

filebrowser config set \
  --theme dark \
  -d /home/carlos/.config/filebrowser/filebrowser.db
  

Reiniciar FileBrowser

sudo service filebrowser start  

Crear pidfile con los permisos correctos

sudo touch /var/run/filebrowser.pid
sudo chown carlos:carlos /var/run/filebrowser.pid
sudo chmod 644 /var/run/filebrowser.pid

Seguridad adicional

Deshabilitar registro público

filebrowser config set --signup false -d /home/carlos/.config/filebrowser/filebrowser.db
 

Configurar límite de sesión

filebrowser config set --auth.recaptcha.secret "" -d /home/carlos/.config/filebrowser/filebrowser.db

Copia de seguridad de configuración

Copia de seguridad de base de datos

cp /home/carlos/.config/filebrowser/filebrowser.db /home/carlos/.config/filebrowser/filebrowser.db.backup

Copia de seguridad de archivos

tar -czf /home/carlos/fileserver-backup-$(date +%Y%m%d).tar.gz /home/carlos/fileserver/

Resumen de comandos de gestión

Reiniciar servicio

sudo service filebrowser restart

Detener FileBrowser completamente

sudo service filebrowser stop
sudo pkill -9 filebrowser

Limpiar el pidfile

rm -f /home/carlos/.filebrowser.pid

Iniciar FileBrowser limpiamente

sudo service filebrowser start 

Verificar que solo hay un proceso

sudo ps aux | grep "filebrowser"
carlos   1322  0.0  0.4 1279096 29736 v0- I   Sun23  1:11.45 /usr/local/bin/filebrowser -c /home/carlos/.config/filebrowser/config.json

Ver logs en tiempo real

tail -f /home/carlos/.config/filebrowser/filebrowser.log

Listar usuarios. Hay que detener FileBrowser antes

sudo service filebrowser stop
sudo pkill -9  filebrowser
filebrowser users ls -d /home/carlos/.config/filebrowser/filebrowser.db

Ver configuración completa

filebrowser config cat -d /home/carlos/.config/filebrowser/filebrowser.db

Navegador local

http://localhost:8080

Red local

http://192.168.88.183:8080

Acceso remoto desde Internet (con Tailscale conectado) Sin Tailscale no funcionará (esto es correcto por seguridad).

http://100.69.99.114:8080
https://https://tornado.tail0788a4.ts.net

Configuración completada exitosamente: FileBrowser instalado y configurando Autenticación habilitada (usuario: carlos) Acceso desde red local (192.168.88.0/24) Acceso remoto seguro vía Tailscale Firewall PF configurado correctamente Autoarranque habilitado (rc.conf)

Tailscale HTTPS (certificados automáticos de Tailscale)

Tailscale puede proporcionar certificados HTTPS válidos de forma automática para un nodo.

Esto generará certificados en /var/lib/tailscale/certs/ o /usr/local/tailscale/certs/. Si no existe el directorio, Tailscale los colocará en el directorio actual.

Obtener el nombre MagicDNS de su nodo

tailscale status

Desde cualquier dispositivo con Tailscale conectado

https://tornado.tail0788a4.ts.net

¡El certificado será válido y reconocido por todos los navegadores!

Usar Tailscale Serve (proxy HTTPS automático)

sudo tailscale serve --bg http://127.0.0.1:8080

Exponer FileBrowser con HTTPS automático en

https://tornado.tail0788a4.ts.net

No es necesario configurar nada más, Tailscale maneja todo el HTTPS

Comprobar

tailscale serve status
https://tornado.tail0788a4.ts.net (tailnet only)
|-- / proxy http://127.0.0.1:8080

Obtener su URL completa

tailscale status --json | grep tornado    
    "DNSName": "tornado.tail0788a4.ts.net.",
    "tornado.tail0788a4.ts.net"

Hacer permanente la configuración

Para que Tailscale Serve se mantenga después de reiniciar:

Crear un servicio rc.d

sudo vim /usr/local/etc/rc.d/tailscale_serve

#!/bin/sh
# PROVIDE: tailscale_serve
# REQUIRE: tailscaled filebrowser
# KEYWORD: shutdown

. /etc/rc.subr

name="tailscale_serve"
rcvar="tailscale_serve_enable"

load_rc_config $name

: ${tailscale_serve_enable:="NO"}

start_cmd="${name}_start"
stop_cmd="${name}_stop"

tailscale_serve_start()
{
    echo "Starting Tailscale Serve..."
    /usr/local/bin/tailscale serve --bg http://127.0.0.1:8080
}

tailscale_serve_stop()
{
    echo "Stopping Tailscale Serve..."
    /usr/local/bin/tailscale serve reset
}

run_rc_command "$1"

Otorgar permisos

sudo chmod +x /usr/local/etc/rc.d/tailscale_serve

Habilitar tailscale_serve

sudo sysrc tailscale_serve_enable="YES"
sudo service tailscale_serve start

Habilitar HTTPS y Serve en Tailscale

Ir a esta URL desde el navegador:

https://login.tailscale.com/admin/machines

Encontrar su máquina (por ejemplo, "tornado")

Hacer clic en los 3 puntos -> "Enable HTTPS"

En la página que se abre: Marcar "HTTPS certificates" (requerido) NO marque "Funnel" (a menos que quiera acceso público desde Internet)

Nota: Serve = Acceso solo desde tu Tailnet (privado) - Esto es lo que quiere

Hacer clic en "Enable HTTPS and Serve" o "Enable"

Volver a configurar Serve

sudo tailscale serve --bg http://127.0.0.1:8080

Debería funcionar sin errores

Verificar serve

tailscale serve status 
https://tornado.tail0788a4.ts.net (tailnet only)
|-- / proxy http://127.0.0.1:8080

Desde cualquier dispositivo con Tailscale

https://tornado.tail0788a4.ts.net

Cuando habilita HTTPS en Tailscale, el nombre de su dispositivo (tornado.tail0788a4.ts.net) se escribirá en un Certificate Transparency Log público. Esto es un requisito de Let's Encrypt y no se puede evitar. Esto NO expone:

Su IP
Sus archivos
El contenido de tu servidor

Solo registra:

El nombre del dispositivo (tornado.tail0788a4.ts.net)

Comprobar que MagicDNS está habilitado

sudo tailscale status --json | grep -i magic
    "InMagicSock": false,
  "MagicDNSSuffix": "tail0788a4.ts.net",
    "MagicDNSSuffix": "tail0788a4.ts.net",
    "MagicDNSEnabled": true
      "InMagicSock": true,
      "InMagicSock": true,
      "InMagicSock": true,
      "InMagicSock": true,

Ver logs de error

sudo tail -50 /home/carlos/.config/filebrowser/filebrowser.log 

Configuración final completada

Elemento		  Valor
URL de acceso		  https://tornado.tail0788a4.ts.net
Certificado SSL 	  Let's Encrypt (válido, sin advertencias)
FileBrowser		  HTTP en localhost:8080
Tailscale Serve		  HTTPS proxy en puerto 443
Acceso			  Solo desde su Tailnet (privado)
Usuario			  carlos
Usuario no privilegiado	  invitado
FreeBSD es genial!.

jueves, 12 de marzo de 2026

Solucionar error librería compartida not found Alacritty FreeBSD 14.3

Solucionar error Shared object "libLLVM.so.19.1" not found Alacritty FreeBSD 14.3

alacritty &
[1] 87350

 libEGL warning: MESA-LOADER: failed to open iris: Shared object "libLLVM.so.19.1" \
 not found, required by "iris_dri.so" (search paths /usr/local/lib/dri, suffix _dri)

libEGL warning: MESA-LOADER: failed to open zink: Shared object "libLLVM.so.19.1" \
not found, required by "zink_dri.so" (search paths /usr/local/lib/dri, suffix _dri)

libEGL warning: MESA-LOADER: failed to open swrast: Shared object "libLLVM.so.19.1" \
not found, required by "swrast_dri.so" (search paths /usr/local/lib/dri, suffix _dri)

Error: Error { raw_code: Some(12289), raw_os_message: None, kind: InitializationFailed }

[1] + 87350 exit 1   alacritty

Causa

Los controladores iris (Intel), zink (capa de traducción Vulkan) y swrast (software rasterizador) fueron compilados contra LLVM 19, pero la librería compartida correspondiente no está instalada en el sistema y si lo está el sistema no la encuentra.

Comprobar que LLVM19 esta instalado

pkg info | grep llvm
linux-rl9-llvm-20.1.8          The Low Level Virtual Machine suite (Rocky Linux 9.7)
llvm18-18.1.8_2                LLVM and Clang
llvm19-19.1.7_1                LLVM and Clang
spirv-llvm-translator-llvm19-19.1.14 Bi-directional translation between SPIR-V and LLVM IR

Comprobar que el archivo existe en la ruta que por defecto es buscado por el programa

ls -l /usr/local/lib/libLLVM.so.19.1
ls: /usr/local/lib/libLLVM.so.19.1: No such file or directory

Verificar que la existencia real de la biblioteca

find /usr/local -name "libLLVM.so.19.1" 2>/dev/null 

/usr/local/llvm19/lib/libLLVM.so.19.1

existe, pero está en /usr/local/llvm19/lib/, un directorio que no está en las rutas de búsqueda por defecto del enlazador dinámico. Hay que hacer que el sistema la encuentre

Solución definitiva

Esta es la opción más ordenada y escalable si en el futuro se necesitan más bibliotecas de LLVM 19.

Agregar el directorio a la configuración de ldconf


Crear archivo de configuración para ldconfig

echo "/usr/local/llvm19/lib" | sudo tee /usr/local/libdata/ldconfig/llvm19.conf

Actualizar la caché de bibliotecas compartidas

sudo ldconfig -m /usr/local/llvm19/lib

Comprobar que se resuelve la dependencia

ldd /usr/local/lib/dri/iris_dri.so | grep LLVM
	libLLVM.so.19.1 => /usr/local/llvm19/lib/libLLVM.so.19.1 (0x13cd9c800000)

Abrir Alacritty

 
alacritty &

Muestra información de versión al iniciar

alacritty -v
Created log file at "/tmp/Alacritty-2576.log"
[0.000017635s] [INFO ] [alacritty] Welcome to Alacritty
[0.000143511s] [INFO ] [alacritty] Version 0.16.1
[0.000156837s] [INFO ] [alacritty] Running on X11
[0.002709829s] [INFO ] [alacritty] Configuration files loaded from:
                       "/home/carlos/.config/alacritty/alacritty.toml"
FreeBSD es genial!.

martes, 10 de marzo de 2026

Formatear y montar dispositivo USB exFAT en FreeBSD 14.3

En FreeBSD 14.3, el sistema no tiene soporte nativo para exFAT en el kernel. Para el montaje de un disco USB exFAT (/dev/da0 o /dev/da0p1 /dev/da0s1), se necesita utilizar FUSE y el puerto fusefs-exfat.

Instalar los paquetes necesarios

pkg install fusefs-libs fusefs-exfat

pkg info | grep -i exfat
exfat-utils-1.4.0_1    Utilities to create, check, label and dump exFAT filesystem
fusefs-exfat-1.4.0_1   Full-featured exFAT FS implementation as a FUSE module

Cargar el módulo FUSE

kldload fusefs

Para cargar siempre al inicio

sysrc kld_list+="fusefs"

Identificar correctamente el dispositivo

dmesg
da0 at umass-sim0 bus 0 scbus1 target 0 lun 0
da0: <ASMT USB 3.0 TOSATA 0> Fixed Direct Access SPC-4 SCSI device
da0: Serial Number 0000000000A3
da0: 400.000MB/s transfers
da0: 38166MB (78165360 512 byte sectors)
da0: quirks=0x2

Particionar /dev/da0 desde FreeBSD 14.3

Desmontar si está montado (por seguridad)

umount /dev/da0p1 2>/dev/null || true 

Formatear dispositivo /dev/da0

Borrar todo y crear tabla GPT (destructivo)

sudo gpart destroy -F da0
da0 destroyed

gpart create -s gpt da0 
da0 created

Agregar una partición que ocupe todo el disco con el tipo ms-basic-data que Windows/macOS reconocen como exFAT

gpart add -t ms-basic-data -l "MiSSD" da0
da0p1 added

Formatear la partición (da0p1)

mkexfatfs /dev/da0p1
mkexfatfs 1.4.0
Creating... done.
Flushing... done.
File system created successfully.

Crear punto de montaje y montar la particion

mkdir /mnt/usb
mount.exfat /dev/da0p1 /mnt/usb
FUSE exfat 1.4.0 (libfuse2)

Mostrar espacio libre en disco

df -h /mnt/usb 
Filesystem    Size   Used   Avail Capacity  Mounted on
/dev/da0p1     37G   1.6M     37G     0%    /mnt/usb

Listar las particiones

gpart show da0
=>      40  78165280  da0  GPT  (37G)
        40  78165280    1  ms-basic-data  (37G)

El dispositivo real suele ser

/dev/da0p1 (GPT con letra p)
/dev/da0s1 (MBR/slice con letra s)

Es importante desmontar antes de desconectar

umount /mnt/usb
FreeBSD es genial!.

jueves, 26 de febrero de 2026

mount FreeBSD Cifrado ZFS desde liveCD y encontrar partición root

Montar FreeBSD 14.3 cifrado ZFS desde liveCD

 mkdir /tmp/mountado
 geli attach /dev/nda0p4
 zpool import -f -R /tmp/montado zroot
 zfs list zroot
 NAME                            USED  AVAIL  REFER  MOUNTPOINT
zroot                            223G   211G    96K  /zroot
zroot/ROOT                      82.9G   211G    96K  none
zroot/ROOT/14.3-RELEASE-p8         8K   211G  21.1G  /
zroot/ROOT/default              82.9G   211G  21.3G  /
zroot/cache                     3.04G   211G    96K  /zroot/cache
zroot/cache/ccache              3.04G   211G  3.04G  /var/cache/ccache
zroot/home                      75.3G   211G    96K  /home
zroot/home/carlos               75.3G   211G  67.4G  /home/carlos
zroot/reserved                  50.0G   261G    96K  /zroot/reserved
zroot/tmp                        320K   211G   180K  /tmp
zroot/usr                       9.89G   211G    96K  /usr
zroot/usr/ports                 6.99G   211G  2.91G  /usr/ports
zroot/usr/src                   2.90G   211G  2.63G  /usr/src
zroot/var                       1.61G   211G    96K  /var
zroot/var/audit                   96K   211G    96K  /var/audit
zroot/var/crash                 1.60G   211G  1.60G  /var/crash
zroot/var/log                   3.11M   211G  2.00M  /var/log
zroot/var/mail                   356K   211G   260K

Propiedad canmount de zroot/ROOT/default

zfs get mountpoint,canmount,mounted zroot/ROOT/default
NAME                PROPERTY    VALUE       SOURCE
zroot/ROOT/default  mountpoint  /           local
zroot/ROOT/default  canmount    noauto      local
zroot/ROOT/default  mounted     yes         -

¿Qué hace canmount=noauto?

La propiedad canmount en ZFS controla si un dataset puede ser montado. Tiene tres valores posibles:

on - El dataset se monta automáticamente cuando es necesario (por ejemplo, al importar el pool o al ejecutar zfs mount -a).

off - El dataset no puede ser montado en absoluto.

noauto - El dataset no se monta automáticamente por operaciones generales del sistema (como zfs mount -a o la importación del pool), pero puede ser montado manualmente con zfs mount. Es un estado intermedio que lo hace "disponible pero no automático".

Montar zroot/ROOT/default con el comando mount

 zfs mount zroot/ROOT/default

Despues de finalizar los cambios exportar el zpool

zpool export zroot

Reiniciar y arrancar desde el disco duro NVMe (nda0)

 shutdown -r now
FreeBSD es genial!.

jueves, 19 de febrero de 2026

Migración de SSD a disco de mayor capacidad en FreeBSD 14.3 send receive vía USB

Estado actual del sistema

zpool status
zpool list 
zpool list -t all

Identificar discos

geom disk list
camcomtrol devlist

Anotar el disco actual (ada0) y el nuevo disco USB (da0)


Limpiar el disco nuevo completamente

gpart destroy -F da0
dd -if=/dev/zero of=/dev/da0 bs=1M count=100

Crear tabla de particiones GPT

gpart create -s gpt da0

Partición de arranque EFI (si el sistema usa UEFI)

gpart add -t efi -s 260M -l efiboot-new da0

Partición de arranque BIOS/GPT (para compatibilidad legacy)

gpart add -t freebsd-boot -s 512k -l gptboot-new da0

Partición swap

gpart add -t freebsd-swap -s 8G -a 1M -l swap0 da0

Particion ZFS (el resto del disco)

gpart add -t freebsd-zfs -a 1M -l zroot-new da0

Para sistemas UEFI


Montar partición EFI del disco nuevo

newfs_msdos -F 32 -c 1 /dev/da0p1
mount -t msdosfs /dev/da0p1 /mnt

Copiar EFI loader desde el actual disco

mkdir -p /mnt/EFI/BOOT
cp -r /boot/efi/EFI/BOOT /mnt/EFI/

umount /mnt

Crear pool temporal en el nuevo disco

Crear pool temporal con nombre diferente para evitar conflictos
zpool create -o altroot=/mnt \
             -o ashift=12 \
             -O compression=lz4 \
             -O atime=off \
             -O xattr=sa \
             -O acltype=posix \
             -m none \
             zroot-new /dev/da0p3

Verificar

zpool status zroot-new

Listar todos los datasets a migrar

zfs list -H -o name -t filesystem,volume

Tomar snapshot recursivo de todo el pool (Shell zsh)

sudo zfs snapshot -r zroot@migracion

Verificar snapshot creado

zfs list -t snapshot -o name | grep "$migracion" 
zroot@migracion
zroot/ROOT@migracion
zroot/ROOT/14.3-RELEASE-filebrowser-tailscale-serve-certs-https@migracion
zroot/ROOT/14.3-RELEASE_2026-02-14_193200@migracion
zroot/ROOT/default@2026-02-14-19:32:00-0
zroot/ROOT/default@2026-02-17-17:48:09-0
zroot/ROOT/default@migracion
zroot/home@migracion
zroot/home/carlos@migracion
zroot/tmp@migracion
zroot/usr@migracion
zroot/usr/ports@migracion
zroot/usr/src@migracion
zroot/var@migracion
zroot/var/audit@migracion
zroot/var/crash@migracion
zroot/var/log@migracion
zroot/var/mail@migracion
zroot/var/tmp@migracion

Enviar snapshot raíz primero

zfs send -Lec zroot@migration-$(date +%Y%m%d) | \
    zfs receive -Fuv zroot-new

Enviar recursivamente todos los datasets hijos

zfs send -RLec zroot@migration-$(date +%Y%m%d) | \
    zfs receive -Fuv zroot-new

Banderas (flags)

-R Recursivo (todos los datasets hijos)
-L Incluye large blocks
-e Embed data (bloques pequeños inline)
-c Comprime el stream
-F En receive: fuerza rollback si es necesario
-u No monta los datasets al recibirlos
-v Verbose

Comprobar la replicación


Comparar conjuntos de datos entre pools

zfs list -r zroot
zfs list -r zroot-new

Verificar propiedades

zfs get all zroot
zfs get all zroot-new

Cantidad de instantáneas

zfs list -t snapshot -r zroot | wc -l
zfs list -t snapshot -r zroot-new | wc -l

Configurar bootfs en el nuevo pool

zpool set bootfs=zroot-new/ROOT/default zroot-new

Verificar

zpool get bootfs zroot-new

Ajustar cachefile y otros parámetros del pool

zpool set cachefile=/boot/zfs/zpool.cache zroot-new

Ver propiedades del pool original para replicarlas

zpool get all zroot

Montar el dataset raíz del nuevo disco

zfs set mountpoint=/mnt zroot-new/ROOT/default
zfs mount zroot-new/ROOT/default

Verificar /boot/loader.conf

cat /mnt/boot/loader.conf

Debe contener algo como:

zfs_load="YES"
vfs.root.mountfrom="zfs:zroot/ROOT/default"

Si el nombre del pool cambió, actualizar:

sed -i '' 's/zroot-new/zroot/g' /mnt/boot/loader.conf

También verificar /etc/fstab (en ZFS puro suele estar sin referencias a ada0)

cat /mnt/etc/fstab

Cambiar el disco y primer arranque


Apagar el sistema

shutdown -p now

Desconectar el disco viejo Conectar el nuevo disco en el slot interno (ya no como USB) Comprobar en BIOS/UEFI que el nuevo disco sea el primero en el orden de arranque Arrancar

Verificación después del arranque


Comprobar salud del pool

zpool status
zpool list

Verificar que todos los datasets montaron

zfs list -t filesystem

Verificar espacio disponible (debe reflejar el disco nuevo mayor)

df -h

Verificar que el sistema arrancó desde el disco correcto

geom disk list
camcontrol devlist

Limpiar snapshots de migración

zfs destroy -r zroot@migracion

Problemas y posibles soluciones después del primer arranque


Renombrar zpool zroot-new a zroot

zpool import -f zroot-new zroot

Error

zpool export zroot-new Cannot unmount /

Es el error clásico: el dataset raíz (/) está montado desde zroot-new, así que ZFS no puede desmontarlo en caliente

Solución Desde loader prompt o LiveCD


Desde el LiveCD

zpool import -f zroot-new zroot
zpool set bootfs=zroot/ROOT/default zroot
zpool export zroot

zpool import -f zroot-new zroot
   zpool set bootfs=zroot/ROOT/default zroot
   zpool export zroot hecho.

Reiniciar

shutdown -r now

FreeBSD al arrancar y no encontrar zroot buscará pools disponibles. Si el disco nuevo ya está interno, lo encontrará como zroot-new y podrás importarlo con el nombre correcto.

Después de reiniciar sin el LiveCD el sistema arranca desde el nuevo disco duro.

Estos dos errores son normales y esperables en este proceso.

1. swapon: /dev/da0p3: No such file or directory, 
2. Import of zpool cache /etc/zfs/zpool.cache failed

El archivo /etc/fstab del sistema nuevo todavía referencia el dispositivo del disco USB (da0) que ya no existe. Ahora el disco está interno, probablemente como ada0 o nvme0ns1.

Montar el sistema de ficheros rw

mount -u -o rw /

Averiguar cómo se llama el disco ahora geom disk list o camcontrol devlist.

geom disk list                                    
Geom name: ada0
Providers:
1. Name: ada0
   Mediasize: 250059350016 (233G)
   Sectorsize: 512
   Mode: r3w3e7
   descr: WDC WDS250G2B0A-00SM50
   lunid: 5001b448b8a2a91f
   ident: 192686806761
   rotationrate: 0
   fwsectors: 63
   fwheads: 16

Cambiar la línea de swap, por ejemplo

/dev/da0p3  none   swap  sw   0   0
 por
/dev/ada0p3  none   swap  sw   0   0

Mejor aún, usar la etiqueta (label) GPT que es independiente del nombre del dispositivo

gpart show -l ada0
=>       40  488397088  ada0  GPT  (233G)
         40     532480     1  efiboot0  (260M)
     532520       1024     2  gptboot0  (512K)
     533544        984        - free -  (492K)
     534528   16777216     3  swap0  (8.0G)
   17311744  471085056     4  zroot-new  (225G)
  488396800        328        - free -  (164K)
ls /dev/gpt/
efiboot0 gptboot0 swap0

En el archivo /etc/fstab usar la etiqueta en lugar del dispositivo

...
/dev/gpt/swap0	  none	  swap	  sw	 0	0
...

Error 2

Import of zpool cache /etc/zfs/zpool.cache failed

El zpool.cache fue generado cuando el pool se llamaba zroot-new y estaba en da0. Ahora el pool se llama zroot y el dispositivo cambió. El caché está obsoleto.

Sencillamente borrarlo, ZFS lo regenera automáticamente

rm /etc/zfs/zpool.cache

Antes de reiniciar para verificar el nombre correcto del dispositivo

swapinfo -h
Device              Size     Used    Avail Capacity
/dev/gpt/swap0      8.0G       0B     8.0G     0%

Si el sistema arrancó pero con errores no críticos Ambos errores no impiden el funcionamiento del sistema. El swap simplemente no está activo hasta que corrijas el fstab, y el zpool.cache se regenera solo. Si llegaste al prompt de login, la migración fue exitosa.

Reiniciar

shutdown -r now

Error 3.

Can't open /dev/gpt/efiboot0

Ese error indica que el bootloader está buscando la partición EFI por su label GPT (efiboot0) pero no la encuentra, probablemente porque el label en el disco nuevo es diferente o no se asignó correctamente

ls /dev/gpt
efiboot0 gptboot0 swap0

Montar el sistema de ficheros rw

mount -u -o rw /

Solución. Renombrar el label al esperado

...
/dev/gpt/efiboot0    /boot/efi	  msdosfs    rw	   2	2
...

Corregir el label

gpart modify -i 1 -l efiboot0 ada0

Si la partición EFI estaba vacía, reinstalar loader

newfs_msdos -F 32 -c 1 /dev/ada0p1
mount -t msdosfs /dev/ada0p1 /mnt
mkdir -p /mnt/EFI/BOOT
cp /boot/loader.efi /mnt/EFI/BOOT/BOOTX64.EFI

umount /mnt

 

FreeBSD es genial!.