Self-hosted applications on a home server using Podman Compose.
| App | Description | Port | Dependencies |
|---|---|---|---|
| Caddy | Reverse proxy with HTTPS | 443 | |
| Uptime Kuma | Uptime monitoring | 3001 | |
| Memos | Note-taking | 5230 | |
| IT-Tools | Developer utilities | 8080 | |
| md-to-pdf | Markdown to PDF | 8000 | |
| SearXNG | Meta search engine | 8888 | Valkey |
| LibreChat | AI chat interface | 3080 | MongoDB |
| Ollama | Local LLM server | 11434 | |
| ConvertX | File converter | 3000 | |
| OpenClaw | AI assistant | 18789 |
make deploy app=memos # Deploy a single app
make stop app=memos # Stop a single app
make up # Start all apps
make down # Stop all apps
make logs app=memos # View app logs
make status app=memos # Check app status
make update # Pull latest images and restart all appsPushing to main triggers a GitHub Actions workflow that SSHs into the server via Tailscale and deploys changed apps.
| Layer | Tool | Purpose |
|---|---|---|
| Private access | Tailscale | Access all apps from anywhere via VPN |
| Reverse proxy | Caddy | Route *.meore.link subdomains to container ports |
| DNS | NextDNS | Resolve *.meore.link to Tailscale IP within tailnet |
| TLS certificates | Cloudflare DNS challenge | HTTPS via Let's Encrypt without exposing ports |
Apps are accessible at https://<app>.meore.link from any device on the tailnet.
Everything below requires a temporary keyboard and monitor (HDMI). Once SSH and Tailscale are working, unplug them.
1. BIOS
Press DEL or F7 during boot to enter BIOS. Configure:
- Boot order: set USB drive first (for OS installation)
- Restore on AC Power Loss: set to Power On (auto-boot after outage)
On some boards this is called "State After G3" under Advanced > PCH-IO Configuration. Set it to S0.
2. Install Fedora Server
Download the Fedora Server DVD ISO (x86_64) and flash it to a USB stick.
Boot from USB and install with:
- Storage: automatic partitioning, full disk, encrypt my data (LUKS)
- Root account: disabled
- User: create one with administrator privileges
After install, log in and connect to Wi-Fi:
nmcli device wifi list
nmcli device wifi connect "SSID" password "PASSWORD"If Wi-Fi doesn't work out of the box, use ethernet or phone USB tethering to get online, then run sudo dnf upgrade -y and reboot. The system update fixes most driver issues on newer hardware.
3. Tailscale
Tailscale gives you remote access from anywhere without port forwarding, static IPs, or firewall configuration.
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --sshOpen the URL it prints, authenticate, and the machine joins your tailnet. From any other device on the same tailnet:
ssh username@hostnameCockpit web UI is also available at https://hostname:9090.
Install Tailscale on your laptop and phone too. After this, unplug keyboard and monitor.
The GitHub Actions deploy workflow connects to your tailnet via OAuth.
In the Tailscale admin console:
- Go to Access Controls > Tags and create two tags (owner:
autogroup:admin):ciserver
- Go to Machines, find your server, and assign
tag:serverto it - In the ACL editor, add the SSH rules:
"ssh": [
{
"action": "check",
"src": ["autogroup:member"],
"dst": ["tag:server"],
"users": ["autogroup:nonroot", "root"]
},
{
"action": "check",
"src": ["autogroup:member"],
"dst": ["autogroup:self"],
"users": ["autogroup:nonroot", "root"]
},
{
"action": "accept",
"src": ["tag:ci"],
"dst": ["tag:server"],
"users": ["deploy"]
}
]- Go to Settings > OAuth clients > Generate OAuth client
- Scope:
auth_keys(writable) - Tag:
tag:ci
- Scope:
- Add the following secrets to your GitHub repo (Settings > Secrets and variables > Actions):
TS_OAUTH_CLIENT_ID- OAuth client IDTS_OAUTH_SECRET- OAuth client secret (shown only once)SSH_USER-deploySSH_HOST- your server's Tailscale hostname
4. LUKS + TPM auto-unlock
With LUKS enabled, the server asks for a passphrase on every boot. This prevents unattended restarts after power loss. Binding the LUKS key to the TPM chip fixes this.
# install tools
sudo dnf install -y tpm2-tools tpm2-tss
# verify TPM works
tpm2_pcrread
# find the LUKS partition
lsblk -f # look for crypto_LUKS, e.g. /dev/nvme0n1p3
# enroll TPM with PCRs 1+7
sudo systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=1+7 /dev/nvme0n1p3Edit /etc/crypttab, change:
luks-xxxxxx UUID=xxxxxx none luks
to:
luks-xxxxxx UUID=xxxxxx - tpm2-device=auto
Rebuild initramfs and reboot:
sudo dracut --regenerate-all --force
sudo rebootThe server should now boot without prompting for a passphrase. The original LUKS password still works as a fallback if TPM unlock ever fails.
5. System packages
sudo dnf upgrade -y
sudo dnf install -y podman-compose git make micro btop6. Deploy user
Create a dedicated user for CI deployments:
sudo useradd -m -s /bin/bash deploy
sudo usermod -aG systemd-journal deployEnable lingering so rootless Podman containers start on boot without a login session:
sudo loginctl enable-linger deployEnable the built-in Podman restart service for the deploy user:
sudo systemctl --user --machine=deploy@.host enable podman-restart.serviceThe repo is cloned automatically on the first deploy via GitHub Actions.
7. Firewall and ports
Open port 443 for HTTPS:
sudo firewall-cmd --add-port=443/tcp --permanent
sudo firewall-cmd --reloadAllow rootless Podman to bind privileged ports (needed for Caddy on 443):
echo "net.ipv4.ip_unprivileged_port_start=80" | sudo tee /etc/sysctl.d/caddy.conf
sudo sysctl -p /etc/sysctl.d/caddy.conf8. NextDNS rewrite
NextDNS resolves *.meore.link to your server's Tailscale IP so the domain works within your tailnet.
- Go to NextDNS and open the profile used by your tailnet
- Go to Settings > Rewrites
- Add a rewrite:
*.meore.link-> your server's Tailscale IP (100.x.x.x) - In the Tailscale admin console, go to DNS > Nameservers and add NextDNS with your profile endpoint ID
- Enable "Override local DNS"
Verify from any tailnet device:
dig +short memos.meore.link
# should return your server's Tailscale IPTPM auto-unlock stopped working
Happens after BIOS or firmware updates. Need temporary keyboard and monitor.
-
Type LUKS password at the boot prompt
-
Re-enroll TPM:
sudo systemd-cryptenroll --wipe-slot=tpm2 --tpm2-device=auto --tpm2-pcrs=1+7 /dev/nvme0n1p3 sudo dracut --regenerate-all --force sudo reboot
Wi-Fi connects but no internet
Common with newer Intel Wi-Fi chips on fresh Fedora installs. The card associates with the access point but cannot route traffic.
- Use phone USB tethering or a different Wi-Fi network (hotspot) as a workaround
- Run
sudo dnf upgrade -yto update kernel and firmware - Reboot, reconnect to the original Wi-Fi
Disabling Wi-Fi power save can also help:
sudo iw dev wlp173s0f0 set power_save offServer does not auto-boot after power loss
Two possible causes:
- BIOS: "Restore on AC Power Loss" is not set to Power On
- LUKS passphrase prompt is waiting for input (set up TPM auto-unlock to fix this)
Cannot reach Cockpit
sudo systemctl status cockpit.socket
sudo firewall-cmd --add-service=cockpit --permanent
sudo firewall-cmd --reloadAccess via Tailscale hostname: https://hostname:9090
Note
Current hardware: Beelink GTi14 Ultra. Intel Core Ultra 9 185H, 32GB DDR5, 1TB NVMe.
This project is licensed under the MIT License.