Am mutat serviciile self-hosted de pe Linux Mint pe un Intel NUC
De ceva timp rulam mai multe servicii self-hosted direct pe calculatorul meu cu Linux Mint. Funcționa, dar cu un dezavantaj major: dacă stingeam calculatorul, se opreau și serviciile. Nu tocmai ideal când vrei să accesezi FreshRSS sau Wallabag de pe telefon la miezul nopții.
Soluția evidentă era un server dedicat care să ruleze non-stop. Am ales un Intel NUC cu procesor Celeron J4025 și 4GB RAM - modest, dar perfect pentru uz personal. Consum mic de energie, fără zgomot, stă ascuns undeva pe un raft. Pe el am instalat Ubuntu Server 24.04.
Acest articol documentează tot procesul: de la instalarea Docker până la Tailscale, Caddy și Vaultwarden.
Starea inițială - ce rulam pe Mint
Pe Linux Mint aveam organizate serviciile în ~/.services/, fiecare cu propriul docker-compose.yml:
| Serviciu | Port | Descriere |
|---|---|---|
| Domain Locker | 3000 | Monitorizare domenii |
| FreshRSS | 8080 | Agregator RSS |
| Linkding | 9090 | Manager de bookmark-uri |
| Booktracker | 2341 | Jurnal de lectură |
| Wallabag | 6500 | Read-it-later |
Toate cu restart: unless-stopped și volume Docker gestionate de engine.
Pasul 1 - Instalarea Docker pe NUC
Mă conectez la NUC prin SSH și instalez Docker din repository-ul oficial:
# Actualizează lista de pachete și instalează dependințele
sudo apt update
sudo apt install -y ca-certificates curl gnupg
# Adaugă cheia GPG oficială Docker
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Adaugă repository-ul Docker
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Instalează Docker Engine + Compose plugin
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Adaug userul meu în grupul docker ca să nu fie nevoie de sudo la fiecare comandă:
sudo usermod -aG docker $USER
Mă deconectez și reconectez prin SSH, apoi verific:
docker --version
docker compose version
docker run hello-world
Output-ul Hello from Docker! confirmă că totul e în regulă.
Pasul 2 - Structura de directoare
Pe un server, convenția e diferită față de un desktop. În loc de ~/.services/ (director ascuns în home), am ales /opt/services/ - standard Linux pentru aplicații third-party, accesibil logic, separat de fișierele personale.
Pe NUC:
sudo mkdir -p /opt/services/{domain-locker,freshrss,linkding,booktracker,wallabag}
sudo chown -R thinkroot:thinkroot /opt/services
Pasul 3 - Copierea fișierelor Compose
De pe Mint, copiez fișierele docker-compose.yml pe NUC via SCP:
scp ~/.services/domain-locker/docker-compose.yml thinkroot@192.168.1.x:/opt/services/domain-locker/
scp ~/.services/freshrss/docker-compose.yml thinkroot@192.168.1.x:/opt/services/freshrss/
scp ~/.services/linkding/docker-compose.yml thinkroot@192.168.1.x:/opt/services/linkding/
scp ~/.services/booktracker/docker-compose.yml thinkroot@192.168.1.x:/opt/services/booktracker/
scp ~/.services/wallabag/docker-compose.yml thinkroot@192.168.1.x:/opt/services/wallabag/
Pasul 4 - Exportul volumelor de pe Mint
Datele efective (feed-urile RSS, bookmark-urile, cărțile) trăiesc în volume Docker. Le export pe fiecare ca arhivă .tar.gz:
# Pe Mint - export volume
docker run --rm \
-v freshrss_data:/data \
-v $HOME:/backup \
alpine tar czf /backup/freshrss_data.tar.gz -C /data .
docker run --rm \
-v freshrss_extensions:/data \
-v $HOME:/backup \
alpine tar czf /backup/freshrss_extensions.tar.gz -C /data .
docker run --rm \
-v linkding_data:/data \
-v $HOME:/backup \
alpine tar czf /backup/linkding_data.tar.gz -C /data .
docker run --rm \
-v wallabag_data:/data \
-v $HOME:/backup \
alpine tar czf /backup/wallabag_data.tar.gz -C /data .
docker run --rm \
-v wallabag_images:/data \
-v $HOME:/backup \
alpine tar czf /backup/wallabag_images.tar.gz -C /data .
Booktracker stochează datele direct în directorul serviciului (bind mount), nu în volume Docker, deci e suficient să copiez directorul data/:
scp -r ~/.services/booktracker/data thinkroot@192.168.1.x:/opt/services/booktracker/
Transfer arhivele pe NUC:
scp ~/*.tar.gz thinkroot@192.168.1.x:~/
Pasul 5 - Importul volumelor pe NUC
Pornesc întâi serviciile o dată pentru ca Docker să creeze volumele corecte, apoi le opresc și import datele:
# Pe NUC - pornire inițială pentru creare volume
cd /opt/services/freshrss && docker compose up -d
cd /opt/services/linkding && docker compose up -d
cd /opt/services/wallabag && docker compose up -d
# Oprire înainte de import
docker compose -f /opt/services/freshrss/docker-compose.yml down
docker compose -f /opt/services/linkding/docker-compose.yml down
docker compose -f /opt/services/wallabag/docker-compose.yml down
O mică lecție de Docker: numele volumelor create de Compose au prefix dublu (ex. freshrss_freshrss_data), nu simplu (freshrss_data). Verific cu:
docker volume ls | grep -E "freshrss|linkding|wallabag"
Import datele în volumele corecte:
docker run --rm \
-v freshrss_freshrss_data:/data \
-v $HOME:/backup \
alpine sh -c "cd /data && tar xzf /backup/freshrss_data.tar.gz"
docker run --rm \
-v freshrss_freshrss_extensions:/data \
-v $HOME:/backup \
alpine sh -c "cd /data && tar xzf /backup/freshrss_extensions.tar.gz"
docker run --rm \
-v linkding_linkding_data:/data \
-v $HOME:/backup \
alpine sh -c "cd /data && tar xzf /backup/linkding_data.tar.gz"
docker run --rm \
-v wallabag_wallabag_data:/data \
-v $HOME:/backup \
alpine sh -c "cd /data && tar xzf /backup/wallabag_data.tar.gz"
docker run --rm \
-v wallabag_wallabag_images:/data \
-v $HOME:/backup \
alpine sh -c "cd /data && tar xzf /backup/wallabag_images.tar.gz"
Pasul 6 - Pornirea tuturor serviciilor
cd /opt/services/domain-locker && docker compose up -d
cd /opt/services/freshrss && docker compose up -d
cd /opt/services/linkding && docker compose up -d
cd /opt/services/booktracker && docker compose up -d
cd /opt/services/wallabag && docker compose up -d
Verific că totul rulează:
docker ps
Toate containerele apar cu status Up. Serviciile sunt accesibile la 192.168.1.x:PORT din rețeaua locală.
Pasul 7 - Curățarea de pe Mint
Odată confirmat că totul merge pe NUC, șterg totul de pe Mint:
# Oprire containere + ștergere volume
docker compose -f ~/.services/domain-locker/docker-compose.yml down -v
docker compose -f ~/.services/freshrss/docker-compose.yml down -v
docker compose -f ~/.services/linkding/docker-compose.yml down -v
docker compose -f ~/.services/booktracker/docker-compose.yml down -v
docker compose -f ~/.services/wallabag/docker-compose.yml down -v
# Ștergere directoare și arhive temporare
rm -rf ~/.services
rm ~/*.tar.gz
# Dezinstalare Docker
sudo apt purge -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo rm -rf /var/lib/docker /var/lib/containerd
sudo rm /etc/apt/sources.list.d/docker.list
sudo rm /etc/apt/keyrings/docker.gpg
sudo apt autoremove -y
Pasul 8 - Tailscale: acces de oriunde
Serviciile rulează, dar sunt accesibile doar în rețeaua locală. Vreau să le accesez și când sunt în deplasare, fără să expun porturile pe internet.
Tailscale rezolvă asta elegant - creează un VPN mesh privat între dispozitivele mele, cu MagicDNS și certificate SSL automate.
Instalare pe NUC:
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
Se generează un link de autentificare - îl deschid în browser, mă autentific cu contul Tailscale. NUC-ul apare în dashboard cu hostname-ul thinkserver.
Activez HTTPS în Tailscale Admin Console (Settings → HTTPS Certificates), apoi generez certificatele pe NUC:
sudo tailscale cert thinkserver.tailxxxx.ts.net
Aceasta scrie două fișiere în directorul curent:
thinkserver.tailxxxx.ts.net.crtthinkserver.tailxxxx.ts.net.key
Mut certificatele într-un loc logic:
sudo mkdir -p /opt/services/caddy/certs
sudo mv thinkserver.tailxxxx.ts.net.crt /opt/services/caddy/certs/
sudo mv thinkserver.tailxxxx.ts.net.key /opt/services/caddy/certs/
sudo chown -R thinkroot:thinkroot /opt/services/caddy
Instalez Tailscale și pe celelalte dispozitive (Linux Mint, telefon) cu același cont.
Pasul 9 - Caddy: reverse proxy cu HTTPS
Vreau să accesez serviciile prin HTTPS la un singur domeniu, nu prin portul 8080 sau 9090. Instalez Caddy ca reverse proxy.
/opt/services/caddy/Caddyfile:
thinkserver.tailxxxx.ts.net {
tls /certs/thinkserver.tailxxxx.ts.net.crt /certs/thinkserver.tailxxxx.ts.net.key
# Vaultwarden
reverse_proxy vaultwarden:80
}
/opt/services/caddy/docker-compose.yml:
services:
caddy:
image: caddy:latest
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./certs:/certs
networks:
- vaultwarden_default
networks:
vaultwarden_default:
external: true
cd /opt/services/caddy && docker compose up -d
Pasul 10 - Vaultwarden: manager de parole self-hosted
Trecerea de la Bitwarden cloud la Vaultwarden (implementare open-source compatibilă 100% cu clienții Bitwarden) era pe lista mea de ceva timp. Acum că am serverul și HTTPS-ul la locul lui, nu mai am scuze.
/opt/services/vaultwarden/docker-compose.yml:
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
ports:
- "8222:80"
volumes:
- vaultwarden_data:/data
environment:
DOMAIN: "https://thinkserver.tailxxxx.ts.net"
SIGNUPS_ALLOWED: "true"
volumes:
vaultwarden_data:
cd /opt/services/vaultwarden && docker compose up -d
Accesez https://thinkserver.tailxxxx.ts.net, creez contul, import parolele din Bitwarden (export CSV → import în Vaultwarden). Instalez extensia de browser și aplicația mobilă, configurate cu serverul propriu.
După ce am verificat că totul e importat corect, dezactivez înregistrările noi din Vaultwarden:
environment:
SIGNUPS_ALLOWED: "false"
docker compose up -d --force-recreate
O perioadă am ținut ambele conturi (Bitwarden cloud + Vaultwarden) active în paralel, ca perioadă de tranziție.
Rezultatul final
Structura completă a serverului:
/opt/services/
├── caddy/
│ ├── docker-compose.yml
│ ├── Caddyfile
│ └── certs/
├── domain-locker/
│ └── docker-compose.yml
├── freshrss/
│ └── docker-compose.yml
├── linkding/
│ └── docker-compose.yml
├── booktracker/
│ ├── docker-compose.yml
│ └── data/
├── wallabag/
│ └── docker-compose.yml
└── vaultwarden/
└── docker-compose.yml
Servicii care rulează acum non-stop pe NUC, accesibile din orice loc prin Tailscale (nu toate, dar pe viitor așa o să fie):
| Serviciu | Acces |
|---|---|
| Domain Locker | 192.168.1.x:3000 (local) |
| FreshRSS | 192.168.1.x:8080 (local) |
| Linkding | 192.168.1.x:9090 (local) |
| Booktracker | 192.168.1.x:2341 (local) |
| Wallabag | 192.168.1.x:6500 (local) |
| Vaultwarden | https://thinkserver.tailxxxx.ts.net (Tailscale) |