What you’ll have running: A working self-hosted setup Estimated time: ~2 hours Difficulty: INTERMEDIATE Power usage: Varies
Hardware needed: A dedicated machine (e.g., an old desktop, a mini PC like an Intel NUC or Beelink, or a Raspberry Pi 4/5 with sufficient RAM and storage) running a Linux distribution.
Why This Setup is Worth It
Ever felt like you’re just renting your digital life? Cloud services are convenient, sure, but they come with trade-offs: privacy concerns, vendor lock-in, and a recurring bill that slowly creeps up. This guide is your ticket to reclaiming control. We’re not just installing software; we’re building foundational self-hosted services that enhance your privacy, streamline your digital life, and give you a tangible sense of ownership.
Over a weekend, we’ll tackle three core projects:
- Vaultwarden (Self-hosted Password Manager): Say goodbye to trusting third parties with your most sensitive credentials. Vaultwarden, a lightweight Bitwarden server implementation, gives you a secure, private, and fully controlled password manager.
- Pi-hole (Network-wide Ad Blocker): Tired of ads and trackers cluttering your internet experience? Pi-hole acts as a DNS sinkhole, blocking unwanted content for every device on your network, improving performance and privacy.
- Jellyfin (Open-source Media Server): Ditch commercial streaming platforms for your personal media collection. Jellyfin lets you organize, stream, and share your movies, TV shows, and music across all your devices, anywhere.
These projects aren’t just about saving a few bucks; they’re about learning, empowering yourself, and building a more resilient, private digital infrastructure right in your home.
Prerequisites and Hardware
Before we dive in, let’s ensure you have the right foundation. This guide assumes you have a basic understanding of Linux command-line operations and networking concepts.
Hardware Recommendations
You don’t need a server rack for these projects. A modest machine will do just fine:
- Processor: Any modern multi-core CPU (Intel i3/i5 equivalent or better, or a Raspberry Pi 4/5).
- RAM: 4GB minimum, 8GB+ recommended, especially if you plan to expand with more services or use Jellyfin’s transcoding features.
- Storage: 64GB SSD minimum for the OS and Docker containers. For Jellyfin, you’ll need additional storage for your media files (e.g., a large HDD or a NAS share mounted to the server).
- Network: A reliable gigabit Ethernet connection is highly recommended for your server. Wi-Fi can work but might introduce latency or instability.
💡 Tip: An old laptop makes an excellent homelab server. It has a built-in UPS (battery), keyboard, and screen for initial setup, and is often very power-efficient.
Software Prerequisites
We’ll be leveraging Docker and Docker Compose for easy deployment and management.
- Operating System: A fresh installation of a modern Linux distribution. Ubuntu Server LTS (e.g., 22.04 or 24.04) or Debian Stable are excellent choices.
- SSH Access: Ensure you can SSH into your server. This makes remote management much easier.
- Docker Engine: The core containerization platform.
- Docker Compose: A tool for defining and running multi-container Docker applications.
Let’s get Docker and Docker Compose installed.
# Update your package lists
sudo apt update && sudo apt upgrade -y
# Install necessary packages for Docker
sudo apt install -y ca-certificates curl gnupg lsb-release
# Add Docker's official GPG key
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
# Add the Docker repository to Apt sources
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
# Update Apt package index again
sudo apt update
# Install Docker Engine, containerd, and Docker Compose
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Add your user to the docker group to run Docker commands without sudo
sudo usermod -aG docker $USER
# Log out and log back in (or reboot) for group changes to take effect
# Then verify Docker installationVerification: After logging back in, run:
docker run hello-worldExpected Output:
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
...
Hello from Docker!
This message shows that your installation appears to be working correctly.
...If you see the “Hello from Docker!” message, you’re good to go!
Installation
We’ll create a dedicated directory for our homelab projects and then set up each service using Docker Compose.
mkdir ~/homelab_services
cd ~/homelab_servicesProject 1: Self-hosted Password Manager (Vaultwarden)
Vaultwarden is a lightweight, secure, and open-source Bitwarden server implementation. It’s fully compatible with official Bitwarden clients.
Vaultwarden Installation Steps
Create a directory for Vaultwarden:
mkdir vaultwarden cd vaultwardenCreate
docker-compose.yml:# ~/homelab_services/vaultwarden/docker-compose.yml version: '3.8' services: vaultwarden: image: vaultwarden/server:1.30.5 # Pin to a specific version for stability container_name: vaultwarden restart: unless-stopped ports: - "8000:80" # Host_Port:Container_Port - Access at http://your_server_ip:8000 volumes: - ./vw-data:/data environment: # Replace with a strong, unique string for your admin token # You can generate one with `openssl rand -base64 32` ADMIN_TOKEN: "YOUR_VERY_STRONG_ADMIN_TOKEN_HERE" # Disable new user registrations after initial setup for security SIGNUPS_ALLOWED: "false" # Enable invitations if you want to invite specific users # INVITE_ACCEPTED_EMAIL_ADDRESSES: "user1@example.com,user2@example.com" WEBSOCKET_ENABLED: "true" # Required for real-time sync with clients # Optionally configure SMTP for email verification, password resets # SMTP_HOST: "smtp.your-provider.com" # SMTP_PORT: "587" # SMTP_FROM: "vaultwarden@yourdomain.com" # SMTP_USERNAME: "your_smtp_username" # SMTP_PASSWORD: "your_smtp_password" # SMTP_SSL: "true" # SMTP_TLS: "true"⚠️ Warning: Change
ADMIN_TOKENimmediately to a strong, randomly generated string. If you enableSIGNUPS_ALLOWED: "true", remember to set it back to"false"after you’ve created your initial user accounts to prevent unauthorized sign-ups.Start Vaultwarden:
docker compose up -d
Vaultwarden Verification
Open your web browser and navigate to http://YOUR_SERVER_IP:8000. You should see the Vaultwarden login page.
Expected Output (browser): The Bitwarden login page, prompting for an email and master password.
Project 2: Network-wide Ad Blocker (Pi-hole)
Pi-hole acts as your network’s DNS server, blocking ad and tracker domains before they even reach your devices.
Pi-hole Installation Steps
Go back to the homelab services directory and create a new one for Pi-hole:
cd ~/homelab_services mkdir pihole cd piholeCreate
docker-compose.yml:# ~/homelab_services/pihole/docker-compose.yml version: '3' services: pihole: image: pihole/pihole:2024.05.0 # Pin to a specific version container_name: pihole hostname: pihole # Set a hostname for the container ports: - "53:53/tcp" - "53:53/udp" - "67:67/udp" # Only required if you intend to use Pi-hole's DHCP server - "80:80/tcp" # For the web interface environment: TZ: "America/New_York" # Set your timezone WEBPASSWORD: "YOUR_PIHOLE_WEB_PASSWORD" # Set a strong password for the web interface # DNSMASQ_LISTENING: "all" # Listen on all interfaces # PIHOLE_DNS_: "1.1.1.1;1.0.0.1" # Upstream DNS servers (Cloudflare) # ServerIP: "YOUR_SERVER_IP" # Optional, if you have issues with multiple network interfaces volumes: - ./etc-pihole/:/etc/pihole/ - ./etc-dnsmasq.d/:/etc/dnsmasq.d/ cap_add: - NET_ADMIN # Required for Pi-hole to function properly restart: unless-stopped⚠️ Warning: Change
WEBPASSWORDto a strong, unique password. ReplaceTZwith your actual timezone (e.g.,Europe/London,Asia/Tokyo).Start Pi-hole:
docker compose up -d
Pi-hole Verification
Open your web browser and navigate to http://YOUR_SERVER_IP/admin. You should see the Pi-hole admin login page. Log in with the password you set.
Expected Output (browser): The Pi-hole admin dashboard, showing statistics and configuration options.
Project 3: Open-source Media Server (Jellyfin)
Jellyfin is a free software media system that puts you in control of managing and streaming your media.
Jellyfin Installation Steps
Go back to the homelab services directory and create a new one for Jellyfin:
cd ~/homelab_services mkdir jellyfin cd jellyfinCreate
docker-compose.yml:# ~/homelab_services/jellyfin/docker-compose.yml version: '3.8' services: jellyfin: image: jellyfin/jellyfin:10.8.13 # Pin to a specific version container_name: jellyfin restart: unless-stopped network_mode: "host" # Recommended for Jellyfin to auto-discover devices and for transcoding # If network_mode: "host" causes issues, use specific ports: # ports: # - "8096:8096/tcp" # HTTP traffic # - "8920:8920/tcp" # HTTPS traffic # - "1900:1900/udp" # DLNA # - "7359:7359/udp" # GDM (client discovery) volumes: - ./config:/config # Configuration files - ./cache:/cache # Transcoding cache - /path/to/your/movies:/data/movies:ro # Mount your actual media paths - /path/to/your/tvshows:/data/tvshows:ro # :ro means read-only # Add more media folders as needed environment: # Optional: Specify the user ID and group ID for the container # This helps with file permissions if your media is on a network share # PUID: "1000" # Your user's UID (get with `id -u $USER`) # PGID: "1000" # Your user's GID (get with `id -g $USER`) JELLYFIN_PublishedServerUrl: "http://YOUR_SERVER_IP:8096" # Replace with your server IP # For hardware transcoding (NVIDIA, Intel QuickSync, AMD AMF/VCE) # Check Jellyfin documentation for specific device mappings # For Intel QuickSync (e.g., on NUCs): # devices: # - /dev/dri:/dev/dri # For NVIDIA GPUs: # runtime: nvidia # environment: # NVIDIA_VISIBLE_DEVICES: all # NVIDIA_DRIVER_CAPABILITIES: all💡 Tip: The
network_mode: "host"simplifies setup by giving the container direct access to the host’s network interfaces. If you encounter issues or prefer more isolation, switch to explicitports:mapping. ⚠️ Warning: Replace/path/to/your/moviesand/path/to/your/tvshowswith the actual paths to your media libraries on your host system. The:roensures the container only has read access, preventing accidental deletion.Start Jellyfin:
docker compose up -d
Jellyfin Verification
Open your web browser and navigate to http://YOUR_SERVER_IP:8096. You should see the Jellyfin setup wizard.
Expected Output (browser): The Jellyfin welcome screen, prompting you to set up your server.
Initial Configuration
Now that our services are running, let’s get them configured.
Vaultwarden Initial Setup
- Access Web Interface: Go to
http://YOUR_SERVER_IP:8000. - Create Your First User: Register a new account with your email and a strong master password. This will be your primary Bitwarden account.
- Disable Sign-ups (if not already): If you initially set
SIGNUPS_ALLOWED: "true"in yourdocker-compose.ymlto create your account, edit thedocker-compose.ymlfile to change it toSIGNUPS_ALLOWED: "false".Then restart the container:# ... environment: ADMIN_TOKEN: "YOUR_VERY_STRONG_ADMIN_TOKEN_HERE" SIGNUPS_ALLOWED: "false" # <-- Ensure this is false after initial user creation WEBSOCKET_ENABLED: "true" # ...cd ~/homelab_services/vaultwarden docker compose down && docker compose up -d - Connect Clients: Download the official Bitwarden client for your browser, desktop, or mobile device. During setup, select “Self-Hosted” and enter your server URL:
http://YOUR_SERVER_IP:8000.
Pi-hole Initial Setup
- Access Admin Panel: Go to
http://YOUR_SERVER_IP/adminand log in with yourWEBPASSWORD. - Configure Upstream DNS (Optional but recommended):
- Navigate to “Settings” > “DNS”.
- You can choose your preferred upstream DNS providers (e.g., Cloudflare, Google, Quad9).
- Configure Your Network to Use Pi-hole: This is the most crucial step.
- Option A (Recommended for most home users): Change DNS on your router.
- Log in to your home router’s administration interface (usually
http://192.168.1.1orhttp://192.168.0.1). - Look for “WAN Settings,” “Internet Settings,” “DHCP/DNS,” or “Network Settings.”
- Find the DNS server fields and change the Primary DNS to
YOUR_SERVER_IP. - Set the Secondary DNS to something reliable like
1.1.1.1(Cloudflare) or8.8.8.8(Google) as a fallback, or leave it blank if you want all DNS traffic to go through Pi-hole. - Save changes and reboot your router.
- Log in to your home router’s administration interface (usually
- Option B (Advanced): Enable Pi-hole’s DHCP server.
- In the Pi-hole admin panel, go to “Settings” > “DHCP”.
- Enable the DHCP server. This will make Pi-hole assign IP addresses and tell devices to use itself for DNS. Make sure your router’s DHCP server is disabled if you use this option to avoid conflicts.
- Option C (Per-device): Manually configure DNS on individual devices. This is useful for testing but not practical for a whole network.
- Option A (Recommended for most home users): Change DNS on your router.
Verification: After configuring your network, visit a website known for ads (e.g., a news site). You should see significantly fewer ads. In the Pi-hole dashboard, you’ll start seeing queries being processed and ads blocked.
Jellyfin Initial Setup
- Access Web Interface: Go to
http://YOUR_SERVER_IP:8096. - Follow the Setup Wizard:
- Language: Select your preferred language.
- Create Admin User: Create a username and strong password for your Jellyfin administrator account.
- Add Media Libraries:
- Click “Add Media Library.”
- Select content type (e.g., “Movies”).
- Click the “+” next to “Folders” and add the paths you mounted in your
docker-compose.yml(e.g.,/data/movies,/data/tvshows). - Choose your preferred language and country for metadata.
- Click “OK.”
- Metadata Language & Country: Set your preferred language and country for movie and TV show information.
- Remote Access: Decide whether to allow remote connections. For now, you can keep it local.
- Finish: Complete the wizard.
- Scan Libraries: Jellyfin will automatically start scanning your media. You can monitor progress from the dashboard.
Verification: After the initial scan, you should see your movies and TV shows populated with posters, descriptions, and other metadata on the Jellyfin dashboard. Try playing a video from a web browser or a Jellyfin client app.
Hardening and Security
Self-hosting comes with the responsibility of security. Here are concrete steps to harden your setup.
1. Firewall Configuration (UFW)
Ensure only necessary ports are open to the outside world. By default, your server should only allow SSH (port 22) from your local network.
# Install UFW if not already installed
sudo apt install ufw -y
# Deny all incoming traffic by default
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH from your local network (e.g., 192.168.1.0/24)
sudo ufw allow from 192.168.1.0/24 to any port 22 comment 'Allow SSH from local network'
# Allow ports for our services (adjust if you used different host ports)
# Vaultwarden (if exposed directly, generally not recommended without reverse proxy)
# sudo ufw allow from 192.168.1.0/24 to any port 8000 comment 'Allow Vaultwarden from local network'
# Pi-hole (DNS and Web UI)
sudo ufw allow from 192.168.1.0/24 to any port 53 comment 'Allow Pi-hole DNS from local network'
sudo ufw allow from 192.168.1.0/24 to any port 80 comment 'Allow Pi-hole Web UI from local network'
# Jellyfin (if using specific ports, otherwise host network bypasses UFW)
# sudo ufw allow from 192.168.1.0/24 to any port 8096 comment 'Allow Jellyfin Web UI from local network'
# Enable UFW
sudo ufw enable
# Check UFW status
sudo ufw status verboseExpected Output:
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp ALLOW IN 192.168.1.0/24
53 ALLOW IN 192.168.1.0/24
80/tcp ALLOW IN 192.168.1.0/24⚠️ Warning: Be very careful with
ufw enable. If you haven’t allowed SSH, you might lock yourself out of your server. Always test SSH access from a new terminal before closing your current session.
2. Strong Passwords and Credential Rotation
- All Admin Interfaces: Use unique, strong passwords for Vaultwarden, Pi-hole, Jellyfin, and your server’s root/user accounts.
- Vaultwarden
ADMIN_TOKEN: Change this token periodically (e.g., every 3-6 months). Regenerate a new token, updatedocker-compose.yml, and restart the container. - SSH Keys: Disable password-based SSH login and use SSH keys for authentication.
# On your local machine, generate an SSH key if you don't have one ssh-keygen -t rsa -b 4096 -C "your_email@example.com" # Copy your public key to the server ssh-copy-id username@your_server_ip # On the server, edit SSH config to disable password authentication sudo nano /etc/ssh/sshd_config # Find and change/add: # PasswordAuthentication no # ChallengeResponseAuthentication no # UsePAM no # Optional, but good for security if you don't need PAM sudo systemctl restart sshd
3. Disable Unused Services
Review what’s running on your server. If you don’t need a desktop environment, web server (other than what’s in Docker), or other services, disable them.
# List all services
sudo systemctl list-unit-files --type=service --state=enabled
# Disable a service (e.g., apache2 if you installed it by mistake)
sudo systemctl disable apache2
sudo systemctl stop apache24. Reverse Proxy with SSL (Advanced, but highly recommended for external access)
If you plan to access your services from outside your home network, a reverse proxy (like Nginx Proxy Manager, Caddy, or Traefik) with SSL (Let’s Encrypt) is essential. This allows you to expose services on standard ports (80/443) and secure them with HTTPS, rather than exposing individual, often insecure, ports.
💡 Tip: Nginx Proxy Manager is a great Docker-based option for beginners, providing a web UI to manage reverse proxies and Let’s Encrypt certificates. This is a project for after you’ve got these three running smoothly.
5. Keep Software Updated
Regularly update your host OS and Docker containers.
Maintenance
Regular maintenance is key to a stable and secure homelab.
1. Updating the Host OS
Update your Linux distribution regularly to get security patches and bug fixes.
sudo apt update && sudo apt upgrade -y
sudo apt autoremove -y # Remove unneeded packages2. Updating Docker Containers
We’ve pinned our container images to specific versions (e.g., vaultwarden/server:1.30.5). To update to a newer version:
- Check for new versions: Visit the official Docker Hub pages for
vaultwarden/server,pihole/pihole, andjellyfin/jellyfinto find the latest stable tags. - Edit
docker-compose.yml: Change the image tag to the new version.# Example for Vaultwarden # image: vaultwarden/server:1.30.5 # Old version image: vaultwarden/server:1.31.0 # New version (example) - Pull and Recreate:
cd ~/homelab_services/vaultwarden # Or pihole, or jellyfin docker compose pull # Pulls the new image version docker compose up -d # Stops the old container, recreates with new image, starts it💡 Tip: Consider using a tool like Watchtower to automate Docker container updates, but be cautious with critical services like Vaultwarden – sometimes manual review of changes is better.
3. Backups
Your data is paramount. Implement a backup strategy for your container volumes.
- Vaultwarden: The
./vw-datavolume contains all your password data. - Pi-hole: The
./etc-piholeand./etc-dnsmasq.dvolumes contain your configurations, blocklists, and query logs. - Jellyfin: The
./configvolume contains your server configuration and metadata. Your media files are separate and should be backed up independently.
Manual Backup Example:
# Stop the container to ensure data consistency
cd ~/homelab_services/vaultwarden
docker compose stop
# Create a timestamped backup of the data volume
tar -czvf ~/backups/vaultwarden_data_$(date +%Y%m%d%H%M%S).tar.gz ./vw-data
# Start the container again
docker compose start
# Repeat for Pi-hole and Jellyfin
# For Pi-hole:
cd ~/homelab_services/pihole
docker compose stop
tar -czvf ~/backups/pihole_config_$(date +%Y%m%d%H%M%S).tar.gz ./etc-pihole ./etc-dnsmasq.d
docker compose start
# For Jellyfin config (media should be backed up separately)
cd ~/homelab_services/jellyfin
docker compose stop
tar -czvf ~/backups/jellyfin_config_$(date +%Y%m%d%H%M%S).tar.gz ./config
docker compose start💡 Tip: Store these backups on a separate machine, external drive, or cloud storage. A backup on the same machine as the original data is not a true backup against hardware failure.
Troubleshooting
Here are some common issues and how to fix them.
General Docker Issues
docker compose up -dfails with port conflict:ERROR: for service_name Cannot start service service_name: driver failed programming external connectivity on endpoint service_name (...): Error starting userland proxy: listen tcp 0.0.0.0:80: bind: address already in useCause: Another service (or another Docker container) on your host machine is already using the port that your Docker container is trying to bind to. Fix:
- Identify what’s using the port:
sudo lsof -i -P -n | grep LISTEN | grep :80(replace80with the conflicting port). - Stop the conflicting service, or change the
host_portin yourdocker-compose.yml(e.g.,8080:80instead of80:80). - Restart your Docker Compose service.
- Identify what’s using the port:
Container not starting or immediately exiting:
Error response from daemon: driver failed programming external connectivity on endpoint ...Cause: Often a misconfiguration in
docker-compose.yml, incorrect volume paths, or missing environment variables. Fix:- Check container logs:
docker compose logs service_name. This will often show the exact error message from the application inside the container. - Run the container interactively for debugging:
docker run -it --rm --entrypoint /bin/bash image_name(e.g.,vaultwarden/server:latest). This lets you explore the container’s environment. - Double-check
docker-compose.ymlfor typos, correct volume paths, and required environment variables.
- Check container logs:
Vaultwarden Specific Issues
Cannot register new users: Cause:
SIGNUPS_ALLOWEDis set to"false". Fix: Temporarily changeSIGNUPS_ALLOWED: "true"indocker-compose.yml, restart the container (docker compose down && docker compose up -d), register your user, then change it back to"false"and restart again.Bitwarden clients can’t connect to
http://YOUR_SERVER_IP:8000: Cause: Firewall blocking port 8000, incorrect IP address, or Vaultwarden container not running. Fix:- Verify Vaultwarden container is running:
docker ps | grep vaultwarden. - Check server firewall:
sudo ufw status. Ensure port 8000 is allowed from your client’s IP range. - Double-check the server IP address.
- Verify Vaultwarden container is running:
Pi-hole Specific Issues
Ads are still showing after Pi-hole setup: Cause: Devices are not using Pi-hole for DNS, or Pi-hole isn’t blocking the specific ad domains. Fix:
- Verify DNS configuration: On a client device, check its DNS server settings. It should point to
YOUR_SERVER_IP. If not, recheck your router’s DNS settings or Pi-hole’s DHCP settings. - Flush client DNS cache:
- Windows:
ipconfig /flushdns - macOS:
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder - Linux:
sudo systemctl restart systemd-resolved(if using systemd-resolved)
- Windows:
- Check Pi-hole logs: In the Pi-hole admin panel, go to “Query Log” to see if queries are reaching Pi-hole and if domains are being blocked. You might need to add more blocklists or manually block specific domains.
- Verify DNS configuration: On a client device, check its DNS server settings. It should point to
Internet access is slow or broken after setting up Pi-hole: Cause: Pi-hole is not running, or its upstream DNS servers are misconfigured/unreachable. Fix:
- Verify Pi-hole container is running:
docker ps | grep pihole. - Check Pi-hole’s upstream DNS settings (Settings -> DNS). Try changing to reliable public DNS like Cloudflare (
1.1.1.1,1.0.0.1) or Google (8.8.8.8,8.8.4.4). - Temporarily revert your router’s DNS settings to public DNS to confirm Pi-hole is the cause.
- Verify Pi-hole container is running:
Jellyfin Specific Issues
Jellyfin can’t find media files: Cause: Incorrect volume mapping in
docker-compose.yml, or file permissions issues on the host system. Fix:- Check
docker-compose.ymlpaths: Ensure/path/to/your/movieson the host side matches the actual location of your media. - Verify permissions: The user ID (
PUID) and group ID (PGID) that Jellyfin runs as inside the container need read access to your media files on the host. If you didn’t setPUID/PGID, the container runs as root. Try settingPUIDandPGIDto your regular user’s ID (id -u $USER,id -g $USER).# Example to fix permissions on host sudo chown -R your_user:your_group /path/to/your/movies sudo chmod -R 755 /path/to/your/movies - Rescan libraries: In the Jellyfin web UI, go to “Dashboard” -> “Libraries” and click the “Scan Library” icon for the affected library.
- Check
Jellyfin streaming is buffering or slow: Cause: Network bottleneck, insufficient CPU for transcoding, or media file issues. Fix:
- Network: Ensure your server and client devices have a good network connection (preferably wired Ethernet).
- Transcoding: If your client device doesn’t support the media’s original format, Jellyfin will transcode it on the fly, which is CPU-intensive.
- Check server CPU usage during playback (
htop). - Consider enabling hardware transcoding if your server supports it (see
devicesorruntime: nvidiaindocker-compose.ymlcomments). - Try optimizing your media files to formats widely supported by clients (e.g., H.264/AAC in an MP4 container) to reduce transcoding needs.
- Check server CPU usage during playback (
- Client Settings: Check client app settings for streaming quality. Lowering the quality can reduce bandwidth and transcoding requirements.
You’ve now got a robust foundation for a self-hosted digital life. These three projects are just the beginning. The skills you’ve gained in setting up Docker Compose, managing containers, and troubleshooting will serve you well as you explore more services and expand your homelab. Remember to keep learning, experimenting, and most importantly, have fun with it!