Merge pull request 'feat(seedbox): Docker + Tailscale sidecar architecture' (#1) from feature/seedbox-docker-tailscale into main
Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
20
seedbox/.env.example
Normal file
20
seedbox/.env.example
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Gitea secrets required for seedbox deployment
|
||||||
|
#
|
||||||
|
# Configure these in: Gitea > Repository > Settings > Secrets and Variables > Actions
|
||||||
|
#
|
||||||
|
# TS_AUTHKEY - Tailscale OAuth client secret (recommended) or auth key
|
||||||
|
# Create at: https://login.tailscale.com/admin/settings/oauth
|
||||||
|
# Required scopes: devices:write
|
||||||
|
# Format: tskey-client-xxxxx-yyyyyyyy
|
||||||
|
#
|
||||||
|
# SEEDBOX_SSH_KEY - SSH private key for deployment
|
||||||
|
# Generate with: ssh-keygen -t ed25519 -f seedbox-deploy -N ""
|
||||||
|
# Add public key to seedbox: ~/.ssh/authorized_keys
|
||||||
|
#
|
||||||
|
# TRANSMISSION_USER - Transmission WebUI username (default: admin)
|
||||||
|
# TRANSMISSION_PASS - Transmission WebUI password
|
||||||
|
|
||||||
|
# Example .env file (DO NOT COMMIT WITH REAL VALUES)
|
||||||
|
TS_AUTHKEY=tskey-client-xxxxx-yyyyyyyy
|
||||||
|
TRANSMISSION_USER=admin
|
||||||
|
TRANSMISSION_PASS=changeme
|
||||||
91
seedbox/.gitea/workflows/deploy.yml
Normal file
91
seedbox/.gitea/workflows/deploy.yml
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
name: Deploy Seedbox
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- 'seedbox/**'
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- 'seedbox/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
name: Deploy Seedbox Stacks
|
||||||
|
runs-on: self-hosted
|
||||||
|
container:
|
||||||
|
image: alpine:latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Install dependencies
|
||||||
|
run: apk add --no-cache openssh-client git
|
||||||
|
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup SSH key
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
echo "${{ secrets.SEEDBOX_SSH_KEY }}" > ~/.ssh/id_ed25519
|
||||||
|
chmod 600 ~/.ssh/id_ed25519
|
||||||
|
# Trust the seedbox host
|
||||||
|
ssh-keyscan -H seedbox.taila5ad8.ts.net >> ~/.ssh/known_hosts 2>/dev/null || true
|
||||||
|
|
||||||
|
- name: Validate compose files (PR only)
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
|
run: |
|
||||||
|
echo "Validating docker-compose files..."
|
||||||
|
for stack in seedbox/stacks/*/; do
|
||||||
|
if [ -f "${stack}docker-compose.yml" ]; then
|
||||||
|
echo "✓ ${stack}docker-compose.yml exists"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "Validation complete."
|
||||||
|
|
||||||
|
- name: Deploy to seedbox
|
||||||
|
if: github.event_name == 'push'
|
||||||
|
run: |
|
||||||
|
ssh -o StrictHostKeyChecking=accept-new debian@seedbox.taila5ad8.ts.net << 'ENDSSH'
|
||||||
|
set -e
|
||||||
|
cd /srv/seedbox
|
||||||
|
|
||||||
|
echo "=== Pulling latest changes ==="
|
||||||
|
git fetch origin main
|
||||||
|
git reset --hard origin/main
|
||||||
|
|
||||||
|
echo "=== Creating .env file ==="
|
||||||
|
cat > .env << 'ENVEOF'
|
||||||
|
TS_AUTHKEY=${{ secrets.TS_AUTHKEY }}
|
||||||
|
TRANSMISSION_USER=${{ secrets.TRANSMISSION_USER }}
|
||||||
|
TRANSMISSION_PASS=${{ secrets.TRANSMISSION_PASS }}
|
||||||
|
ENVEOF
|
||||||
|
chmod 600 .env
|
||||||
|
|
||||||
|
echo "=== Deploying stacks ==="
|
||||||
|
for stack in stacks/*/; do
|
||||||
|
if [ -f "${stack}docker-compose.yml" ]; then
|
||||||
|
stack_name=$(basename "$stack")
|
||||||
|
echo "Deploying ${stack_name}..."
|
||||||
|
docker compose -f "${stack}docker-compose.yml" --env-file .env pull
|
||||||
|
docker compose -f "${stack}docker-compose.yml" --env-file .env up -d --remove-orphans
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "=== Cleanup unused images ==="
|
||||||
|
docker image prune -f
|
||||||
|
|
||||||
|
echo "=== Current status ==="
|
||||||
|
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
|
||||||
|
ENDSSH
|
||||||
|
|
||||||
|
- name: Deployment summary
|
||||||
|
if: github.event_name == 'push'
|
||||||
|
run: |
|
||||||
|
echo "✅ Deployment complete!"
|
||||||
|
echo ""
|
||||||
|
echo "Services should be available at:"
|
||||||
|
echo " • transmission.taila5ad8.ts.net"
|
||||||
|
echo " • portainer.taila5ad8.ts.net"
|
||||||
|
echo " • prowlarr.taila5ad8.ts.net"
|
||||||
|
echo " • sonarr.taila5ad8.ts.net"
|
||||||
@@ -1,214 +1,354 @@
|
|||||||
# Seedbox Server
|
# Seedbox Server
|
||||||
|
|
||||||
Deploys a seedbox with Transmission for maintaining Linux ISO mirrors and OS images.
|
Docker-based seedbox with Tailscale integration. Each service runs in its own container with a Tailscale sidecar for secure HTTPS access via your tailnet.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ SEEDBOX │
|
||||||
|
│ │
|
||||||
|
│ Tailscale Host (SSH only) │
|
||||||
|
│ └─► seedbox.taila5ad8.ts.net │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Docker Stacks │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
|
||||||
|
│ │ │ Transmission│ │ Prowlarr │ │ Sonarr │ │ │
|
||||||
|
│ │ │ + ts-sidecar│ │ + ts-sidecar│ │ + ts-sidecar│ │ │
|
||||||
|
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
|
||||||
|
│ │ │ │ │ │ │
|
||||||
|
│ │ ▼ ▼ ▼ │ │
|
||||||
|
│ │ transmission. prowlarr. sonarr. │ │
|
||||||
|
│ │ taila5ad8.ts.net taila5ad8.ts.net taila5ad8.ts.net │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ ┌─────────────┐ │ │
|
||||||
|
│ │ │ Portainer │ (optional, for monitoring) │ │
|
||||||
|
│ │ │ + ts-sidecar│ │ │
|
||||||
|
│ │ └─────────────┘ │ │
|
||||||
|
│ │ │ │ │
|
||||||
|
│ │ ▼ │ │
|
||||||
|
│ │ portainer.taila5ad8.ts.net │ │
|
||||||
|
│ └─────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ Storage: │
|
||||||
|
│ ├─ /srv/seedbox/downloads (local SSD) │
|
||||||
|
│ └─ /mnt/media (NFS from NAS) │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Server Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
NFS_SERVER=nas TRANSMISSION_PASS=MySecureP@ss123 \
|
# With NFS mount
|
||||||
|
NFS_SERVER=nas curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/seedbox/install.sh | bash
|
||||||
|
|
||||||
|
# Without NFS (configure later)
|
||||||
curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/seedbox/install.sh | bash
|
curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/seedbox/install.sh | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
> **⚠️ Important:** Always set `TRANSMISSION_PASS` explicitly. If omitted, a random password is generated and displayed only once at the end of the installation. It cannot be recovered afterward without resetting the config.
|
### 2. Configure Gitea Secrets
|
||||||
|
|
||||||
## Components
|
Go to **Gitea > Repository > Settings > Secrets and Variables > Actions** and add:
|
||||||
|
|
||||||
- **Transmission**: BitTorrent client with WebUI
|
| Secret | Description | Example |
|
||||||
- **NFS**: Dual mount to NAS for downloads and media storage
|
|--------|-------------|---------|
|
||||||
- **Tailscale**: Private access to WebUI via `tailscale serve`
|
| `TS_AUTHKEY` | Tailscale OAuth client secret | `tskey-client-xxxxx-yyyyyyyy` |
|
||||||
- **Docker**: Container runtime
|
| `SEEDBOX_SSH_KEY` | SSH private key (ed25519) | `-----BEGIN OPENSSH PRIVATE KEY-----...` |
|
||||||
- **UFW**: Firewall (only peer port exposed publicly)
|
| `TRANSMISSION_USER` | Transmission WebUI username | `admin` |
|
||||||
- **fail2ban** + **unattended-upgrades**: Basic hardening
|
| `TRANSMISSION_PASS` | Transmission WebUI password | `your-secure-password` |
|
||||||
|
|
||||||
## Environment Variables
|
#### Creating Tailscale OAuth Client
|
||||||
|
|
||||||
| Variable | Default | Description |
|
1. Go to https://login.tailscale.com/admin/settings/oauth
|
||||||
|----------|---------|-------------|
|
2. Click "Generate OAuth Client"
|
||||||
| `NFS_SERVER` | *required* | NAS hostname/IP (Tailscale) |
|
3. Scopes: `devices:write`
|
||||||
| `NFS_SHARE_DOWNLOAD` | `/volume2/Downloads` | NFS export for downloads |
|
4. Copy the **Client Secret** (starts with `tskey-client-`)
|
||||||
| `NFS_SHARE_MEDIA` | `/volume2/Multimédia` | NFS export for media/ISOs |
|
|
||||||
| `NFS_MOUNT_DOWNLOAD` | `/mnt/download` | Local mount for downloads |
|
|
||||||
| `NFS_MOUNT_MEDIA` | `/mnt/media` | Local mount for media |
|
|
||||||
| `SEEDBOX_HOSTNAME` | `seedbox` | Server hostname |
|
|
||||||
| `PEER_PORT` | `51413` | BitTorrent peer port |
|
|
||||||
| `TRANSMISSION_USER` | `admin` | WebUI username |
|
|
||||||
| `TRANSMISSION_PASS` | *auto-generated* | WebUI password (⚠️ set explicitly!) |
|
|
||||||
| `TZ` | `Europe/Paris` | Timezone |
|
|
||||||
|
|
||||||
Example with custom settings:
|
#### Creating SSH Deploy Key
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
NFS_SERVER=nas \
|
# Generate key pair
|
||||||
NFS_SHARE_DOWNLOAD=/volume1/torrents \
|
ssh-keygen -t ed25519 -f seedbox-deploy -N "" -C "gitea-deploy"
|
||||||
NFS_SHARE_MEDIA=/volume1/iso \
|
|
||||||
TRANSMISSION_USER=damien \
|
# Add public key to seedbox
|
||||||
TRANSMISSION_PASS=MySecurePassword \
|
ssh debian@seedbox.taila5ad8.ts.net "cat >> ~/.ssh/authorized_keys" < seedbox-deploy.pub
|
||||||
curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/seedbox/install.sh | bash
|
|
||||||
|
# Copy private key content to Gitea secret SEEDBOX_SSH_KEY
|
||||||
|
cat seedbox-deploy
|
||||||
```
|
```
|
||||||
|
|
||||||
### Password Recovery
|
#### Tailscale ACL Configuration
|
||||||
|
|
||||||
If you forgot to set `TRANSMISSION_PASS` and lost the auto-generated password:
|
Add this tag to your Tailscale ACL policy (https://login.tailscale.com/admin/acls):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tagOwners": {
|
||||||
|
"tag:container": ["autogroup:admin"]
|
||||||
|
},
|
||||||
|
"acls": [
|
||||||
|
{
|
||||||
|
"action": "accept",
|
||||||
|
"src": ["autogroup:member"],
|
||||||
|
"dst": ["tag:container:*"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Deploy Services
|
||||||
|
|
||||||
|
Push to the `main` branch to trigger deployment:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Option 1: Check the docker-compose.yml (password in clear text)
|
git push origin main
|
||||||
grep PASS ~/transmission/docker-compose.yml
|
```
|
||||||
|
|
||||||
# Option 2: Reset by editing docker-compose.yml
|
Or create a PR for validation first.
|
||||||
cd ~/transmission
|
|
||||||
sed -i 's/PASS=.*/PASS=NewPassword/' docker-compose.yml
|
## Services
|
||||||
docker compose down && docker compose up -d
|
|
||||||
|
| Service | URL | Port | Description |
|
||||||
|
|---------|-----|------|-------------|
|
||||||
|
| Transmission | `transmission.taila5ad8.ts.net` | 9091 | BitTorrent client |
|
||||||
|
| Prowlarr | `prowlarr.taila5ad8.ts.net` | 9696 | Indexer manager |
|
||||||
|
| Sonarr | `sonarr.taila5ad8.ts.net` | 8989 | TV series manager |
|
||||||
|
| Portainer | `portainer.taila5ad8.ts.net` | 9000 | Docker management UI |
|
||||||
|
|
||||||
|
## Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
/srv/seedbox/
|
||||||
|
├── stacks/
|
||||||
|
│ ├── transmission/
|
||||||
|
│ │ ├── docker-compose.yml
|
||||||
|
│ │ └── serve.json
|
||||||
|
│ ├── prowlarr/
|
||||||
|
│ │ ├── docker-compose.yml
|
||||||
|
│ │ └── serve.json
|
||||||
|
│ ├── sonarr/
|
||||||
|
│ │ ├── docker-compose.yml
|
||||||
|
│ │ └── serve.json
|
||||||
|
│ └── portainer/
|
||||||
|
│ ├── docker-compose.yml
|
||||||
|
│ └── serve.json
|
||||||
|
├── downloads/ # Local downloads (SSD)
|
||||||
|
├── .env # Secrets (created by pipeline)
|
||||||
|
└── .gitea/
|
||||||
|
└── workflows/
|
||||||
|
└── deploy.yml # Deployment pipeline
|
||||||
|
```
|
||||||
|
|
||||||
|
## Adding a New Service
|
||||||
|
|
||||||
|
1. Create a new directory in `stacks/`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p seedbox/stacks/myservice
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Create `docker-compose.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
ts-myservice:
|
||||||
|
image: tailscale/tailscale:latest
|
||||||
|
hostname: myservice
|
||||||
|
environment:
|
||||||
|
- TS_AUTHKEY=${TS_AUTHKEY}
|
||||||
|
- TS_EXTRA_ARGS=--advertise-tags=tag:container
|
||||||
|
- TS_STATE_DIR=/var/lib/tailscale
|
||||||
|
- TS_SERVE_CONFIG=/config/serve.json
|
||||||
|
- TS_USERSPACE=false
|
||||||
|
volumes:
|
||||||
|
- ts-state:/var/lib/tailscale
|
||||||
|
- ./serve.json:/config/serve.json:ro
|
||||||
|
devices:
|
||||||
|
- /dev/net/tun:/dev/net/tun
|
||||||
|
cap_add:
|
||||||
|
- net_admin
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
myservice:
|
||||||
|
image: myservice/image:latest
|
||||||
|
container_name: myservice
|
||||||
|
network_mode: service:ts-myservice
|
||||||
|
depends_on:
|
||||||
|
- ts-myservice
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Europe/Paris
|
||||||
|
volumes:
|
||||||
|
- config:/config
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
ts-state:
|
||||||
|
config:
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Create `serve.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"TCP": {
|
||||||
|
"443": {
|
||||||
|
"HTTPS": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Web": {
|
||||||
|
"myservice.taila5ad8.ts.net:443": {
|
||||||
|
"Handlers": {
|
||||||
|
"/": {
|
||||||
|
"Proxy": "http://127.0.0.1:PORT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Commit and push:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add seedbox/stacks/myservice/
|
||||||
|
git commit -m "feat(seedbox): add myservice stack"
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
## Removing a Service
|
||||||
|
|
||||||
|
### Option A: Via Git (recommended)
|
||||||
|
|
||||||
|
1. Remove the stack directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rm -rf seedbox/stacks/myservice/
|
||||||
|
git add -A
|
||||||
|
git commit -m "feat(seedbox): remove myservice stack"
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Manually stop containers on seedbox:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh debian@seedbox.taila5ad8.ts.net
|
||||||
|
cd /srv/seedbox/stacks/myservice
|
||||||
|
docker compose down -v
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Remove Tailscale device from admin console
|
||||||
|
|
||||||
|
### Option B: Manual (emergency)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh debian@seedbox.taila5ad8.ts.net
|
||||||
|
cd /srv/seedbox/stacks/myservice
|
||||||
|
docker compose down -v
|
||||||
|
rm -rf /srv/seedbox/stacks/myservice
|
||||||
|
# Remove device from https://login.tailscale.com/admin/machines
|
||||||
|
```
|
||||||
|
|
||||||
|
## Updating Services
|
||||||
|
|
||||||
|
### Update Docker Images
|
||||||
|
|
||||||
|
The pipeline automatically pulls latest images on each deployment. To force an update:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Trigger a new deployment by making any change
|
||||||
|
git commit --allow-empty -m "chore: trigger deployment"
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Update
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh debian@seedbox.taila5ad8.ts.net
|
||||||
|
cd /srv/seedbox/stacks/transmission
|
||||||
|
docker compose pull
|
||||||
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
## Network Access
|
## Network Access
|
||||||
|
|
||||||
| Service | Public | Tailscale |
|
| Service | Public | Tailscale |
|
||||||
|---------|--------|-----------|
|
|---------|--------|-----------|
|
||||||
| BitTorrent peers | ✅ Port 51413 | ✅ |
|
| SSH | ❌ | ✅ `seedbox.taila5ad8.ts.net` |
|
||||||
| Transmission WebUI | ❌ | ✅ HTTPS via `tailscale serve` |
|
| Transmission WebUI | ❌ | ✅ HTTPS via sidecar |
|
||||||
| SSH | ❌ | ✅ Tailscale SSH |
|
| Transmission Peer | ✅ Port 51413 | ✅ |
|
||||||
| NFS (to NAS) | ❌ | ✅ |
|
| Prowlarr | ❌ | ✅ HTTPS via sidecar |
|
||||||
|
| Sonarr | ❌ | ✅ HTTPS via sidecar |
|
||||||
|
| Portainer | ❌ | ✅ HTTPS via sidecar |
|
||||||
|
|
||||||
### WebUI Access
|
## Troubleshooting
|
||||||
|
|
||||||
The WebUI is exposed via Tailscale Serve with automatic HTTPS:
|
### Check container status
|
||||||
|
|
||||||
```
|
|
||||||
https://seedbox.<your-tailnet>.ts.net
|
|
||||||
```
|
|
||||||
|
|
||||||
The WebUI binds to `localhost:9091` and is proxied through `tailscale serve`, ensuring it's never accessible from the public internet.
|
|
||||||
|
|
||||||
## Storage Architecture
|
|
||||||
|
|
||||||
```
|
|
||||||
NAS (via Tailscale) Seedbox LXC (70GB)
|
|
||||||
┌─────────────────────┐ ┌─────────────────────┐
|
|
||||||
│ /volume2/Downloads │◄──── NFS ────►│ /mnt/download │
|
|
||||||
│ (incomplete + temp) │ │ └► /downloads │
|
|
||||||
├─────────────────────┤ │ (in container) │
|
|
||||||
│ /volume2/Multimédia │◄──── NFS ────►│ /mnt/media │
|
|
||||||
│ (ISOs, VMDK, QCOW) │ │ └► /media │
|
|
||||||
└─────────────────────┘ │ (in container) │
|
|
||||||
└─────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### Transmission Paths
|
|
||||||
|
|
||||||
| Container Path | Host Path | NAS Path | Purpose |
|
|
||||||
|----------------|-----------|----------|---------|
|
|
||||||
| `/downloads` | `/mnt/download` | `/volume2/Downloads` | Incomplete + completed torrents |
|
|
||||||
| `/media` | `/mnt/media` | `/volume2/Multimédia` | Final ISOs, VMDK, QCOW images |
|
|
||||||
|
|
||||||
### Recommended Workflow
|
|
||||||
|
|
||||||
1. Torrents download to `/downloads` (on NAS via NFS)
|
|
||||||
2. Once complete, move ISOs to `/media/iso/<distro>/`
|
|
||||||
3. Proxmox mounts the same NAS share for VM templates
|
|
||||||
|
|
||||||
## What it does
|
|
||||||
|
|
||||||
1. Sets hostname
|
|
||||||
2. Installs base packages (vim, fail2ban, unattended-upgrades, nfs-common, at)
|
|
||||||
3. Installs and connects Tailscale
|
|
||||||
4. Installs Docker
|
|
||||||
5. Configures dual NFS mounts to NAS (same as Proxmox)
|
|
||||||
6. Deploys Transmission container with both mounts
|
|
||||||
7. Exposes WebUI via `tailscale serve` (HTTPS on tailnet)
|
|
||||||
8. Configures UFW (peer port public, WebUI via Tailscale only)
|
|
||||||
9. Temporarily opens SSH port 22 for 5 minutes (safety net)
|
|
||||||
|
|
||||||
## SSH Safety Net
|
|
||||||
|
|
||||||
During installation, SSH port 22 is temporarily opened for 5 minutes to prevent lockout if you're connected via public IP. After 5 minutes, it will be automatically closed and only Tailscale SSH will work.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# List scheduled jobs
|
docker ps -a
|
||||||
sudo atq
|
docker logs ts-transmission
|
||||||
|
docker logs transmission
|
||||||
# Cancel the scheduled SSH closure (replace N with job number)
|
|
||||||
sudo atrm N
|
|
||||||
|
|
||||||
# Manually close SSH port 22 if needed
|
|
||||||
sudo ufw delete allow 22/tcp
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Directory Structure
|
### Check Tailscale sidecar
|
||||||
|
|
||||||
Organize your media by type:
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec ts-transmission tailscale status
|
||||||
|
docker exec ts-transmission tailscale serve status
|
||||||
```
|
```
|
||||||
/mnt/media/
|
|
||||||
├── iso/
|
### Restart a stack
|
||||||
│ ├── debian/
|
|
||||||
│ │ └── debian-12.7.0-amd64-netinst.iso
|
```bash
|
||||||
│ ├── ubuntu/
|
cd /srv/seedbox/stacks/transmission
|
||||||
│ │ └── ubuntu-24.04.1-live-server-amd64.iso
|
docker compose restart
|
||||||
│ ├── rhel/
|
```
|
||||||
│ │ └── rocky-9.4-x86_64-minimal.iso
|
|
||||||
│ └── proxmox/
|
### View all Tailscale devices
|
||||||
│ └── proxmox-ve_8.2-1.iso
|
|
||||||
├── vmdk/
|
```bash
|
||||||
│ └── windows-server-2022.vmdk
|
tailscale status
|
||||||
└── qcow/
|
```
|
||||||
└── cloud-init-debian-12.qcow2
|
|
||||||
|
### Force re-authentication of sidecar
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /srv/seedbox/stacks/transmission
|
||||||
|
docker compose down
|
||||||
|
docker volume rm transmission_ts-state
|
||||||
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
## NAS Configuration (Synology)
|
## NAS Configuration (Synology)
|
||||||
|
|
||||||
Ensure your NAS exports both shares via NFS:
|
Ensure your NAS exports the media share via NFS:
|
||||||
|
|
||||||
1. Control Panel → Shared Folder → Edit → NFS Permissions
|
1. **Control Panel → Shared Folder → Edit → NFS Permissions**
|
||||||
2. For each share (`Downloads` and `Multimédia`), add rule:
|
2. Add rule:
|
||||||
- Hostname/IP: `*` or Tailscale IP of seedbox (e.g., `100.x.x.x`)
|
- Hostname/IP: `100.64.0.0/10` (Tailscale subnet) or specific IP
|
||||||
- Privilege: Read/Write
|
- Privilege: Read/Write
|
||||||
- Squash: No mapping
|
- Squash: No mapping
|
||||||
- Security: sys
|
- Security: sys
|
||||||
- Enable NFSv4.1: ✅
|
- Enable NFSv4.1: ✅
|
||||||
|
|
||||||
## Post-install
|
## Post-install Verification
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Check NFS mounts
|
# Check NFS mount
|
||||||
df -h /mnt/download /mnt/media
|
df -h /mnt/media
|
||||||
|
|
||||||
# View Transmission logs
|
# Check Docker
|
||||||
cd ~/transmission && docker compose logs -f
|
docker ps
|
||||||
|
|
||||||
# Restart Transmission
|
# Check Tailscale devices
|
||||||
cd ~/transmission && docker compose restart
|
tailscale status
|
||||||
|
|
||||||
# Check tailscale serve status
|
# Test service access
|
||||||
tailscale serve status
|
curl -k https://transmission.taila5ad8.ts.net
|
||||||
|
|
||||||
# Move completed ISO to final location
|
|
||||||
mv /mnt/download/debian-12.iso /mnt/media/iso/debian/
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### WebUI not accessible
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check tailscale serve is running
|
|
||||||
tailscale serve status
|
|
||||||
|
|
||||||
# Restart if needed
|
|
||||||
sudo tailscale serve --bg http://localhost:9091
|
|
||||||
|
|
||||||
# Check container is running
|
|
||||||
docker ps | grep transmission
|
|
||||||
|
|
||||||
# Check container logs
|
|
||||||
cd ~/transmission && docker compose logs
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reset Transmission credentials
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ~/transmission
|
|
||||||
docker compose down
|
|
||||||
# Edit docker-compose.yml to change USER and PASS
|
|
||||||
vim docker-compose.yml
|
|
||||||
docker compose up -d
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# install.sh - Automated deployment of Seedbox Server with Transmission
|
# install.sh - Seedbox Server Initial Setup
|
||||||
# Usage: NFS_SERVER=<nas-ip> curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/seedbox/install.sh | bash
|
# Usage: curl -fsSL https://gitea.arnodo.fr/Damien/infra-scripts/raw/branch/main/seedbox/install.sh | bash
|
||||||
|
#
|
||||||
|
# This script prepares the server for Docker-based seedbox deployment.
|
||||||
|
# Services are deployed separately via Gitea Actions.
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -14,6 +17,16 @@ log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
|||||||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||||
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||||
|
|
||||||
|
# Configuration (can be overridden via environment)
|
||||||
|
HOSTNAME="${SEEDBOX_HOSTNAME:-seedbox}"
|
||||||
|
NFS_SERVER="${NFS_SERVER:-}"
|
||||||
|
NFS_SHARE_MEDIA="${NFS_SHARE_MEDIA:-/volume2/Multimédia}"
|
||||||
|
NFS_MOUNT_MEDIA="${NFS_MOUNT_MEDIA:-/mnt/media}"
|
||||||
|
NFS_OPTS="defaults,_netdev,nofail,x-systemd.automount,x-systemd.mount-timeout=30s"
|
||||||
|
SEEDBOX_DIR="/srv/seedbox"
|
||||||
|
DOWNLOADS_DIR="${SEEDBOX_DIR}/downloads"
|
||||||
|
REPO_URL="${REPO_URL:-https://gitea.arnodo.fr/Damien/infra-scripts.git}"
|
||||||
|
|
||||||
# Pre-flight checks
|
# Pre-flight checks
|
||||||
check_root() {
|
check_root() {
|
||||||
if [[ $EUID -eq 0 ]]; then
|
if [[ $EUID -eq 0 ]]; then
|
||||||
@@ -32,83 +45,57 @@ check_debian() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
check_required_vars() {
|
|
||||||
if [[ -z "${NFS_SERVER:-}" ]]; then
|
|
||||||
log_error "NFS_SERVER environment variable is required."
|
|
||||||
log_error "Usage: NFS_SERVER=<nas-ip-or-hostname> curl -fsSL ... | bash"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Generate random password if not provided
|
|
||||||
# Note: uses head -c with || true to avoid SIGPIPE with pipefail
|
|
||||||
generate_password() {
|
|
||||||
head -c 100 /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 16 || true
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get Tailscale FQDN hostname from Self.DNSName
|
|
||||||
# Extracts DNSName from the Self object in tailscale status --json
|
|
||||||
get_tailscale_hostname() {
|
|
||||||
local dns_name
|
|
||||||
# Use awk to extract Self.DNSName specifically (not from peers)
|
|
||||||
# Looks for DNSName within the Self block
|
|
||||||
dns_name=$(tailscale status --json 2>/dev/null | awk -F'"' '
|
|
||||||
/"Self"/ { in_self=1 }
|
|
||||||
in_self && /"DNSName"/ { gsub(/\.$/, "", $4); print $4; exit }
|
|
||||||
' || true)
|
|
||||||
|
|
||||||
if [[ -z "$dns_name" ]]; then
|
|
||||||
# Fallback: use hostname + default tailnet domain hint
|
|
||||||
dns_name="${HOSTNAME}.ts.net"
|
|
||||||
log_warn "Could not determine Tailscale FQDN, using fallback: $dns_name"
|
|
||||||
fi
|
|
||||||
echo "$dns_name"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Configuration variables (can be overridden via environment)
|
|
||||||
HOSTNAME="${SEEDBOX_HOSTNAME:-seedbox}"
|
|
||||||
# NFS shares configuration
|
|
||||||
NFS_SHARE_DOWNLOAD="${NFS_SHARE_DOWNLOAD:-/volume2/Downloads}"
|
|
||||||
NFS_SHARE_MEDIA="${NFS_SHARE_MEDIA:-/volume2/Multimédia}"
|
|
||||||
NFS_MOUNT_DOWNLOAD="${NFS_MOUNT_DOWNLOAD:-/mnt/download}"
|
|
||||||
NFS_MOUNT_MEDIA="${NFS_MOUNT_MEDIA:-/mnt/media}"
|
|
||||||
# NFS mount options (same as Proxmox)
|
|
||||||
NFS_OPTS="defaults,_netdev,nofail,x-systemd.automount,x-systemd.mount-timeout=30s"
|
|
||||||
PEER_PORT="${PEER_PORT:-51413}"
|
|
||||||
TIMEZONE="${TZ:-Europe/Paris}"
|
|
||||||
TRANSMISSION_USER="${TRANSMISSION_USER:-admin}"
|
|
||||||
TRANSMISSION_PASS="${TRANSMISSION_PASS:-$(generate_password)}"
|
|
||||||
TRANSMISSION_DIR="$HOME/transmission"
|
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
log_info "=== Seedbox Server Deployment ==="
|
log_info "=== Seedbox Server Setup ==="
|
||||||
|
|
||||||
check_root
|
check_root
|
||||||
check_debian
|
check_debian
|
||||||
check_required_vars
|
|
||||||
|
|
||||||
|
# Step 1: System update
|
||||||
|
log_info "Updating system..."
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get upgrade -y -qq
|
||||||
|
|
||||||
|
# Step 2: Set hostname
|
||||||
log_info "Setting hostname to: $HOSTNAME"
|
log_info "Setting hostname to: $HOSTNAME"
|
||||||
echo "$HOSTNAME" | sudo tee /etc/hostname > /dev/null
|
echo "$HOSTNAME" | sudo tee /etc/hostname > /dev/null
|
||||||
sudo hostnamectl set-hostname "$HOSTNAME"
|
sudo hostnamectl set-hostname "$HOSTNAME"
|
||||||
|
|
||||||
|
# Step 3: Install base packages
|
||||||
log_info "Installing base packages..."
|
log_info "Installing base packages..."
|
||||||
sudo apt-get update -qq
|
sudo apt-get install -y -qq \
|
||||||
sudo apt-get install -y -qq vim ca-certificates curl gnupg lsb-release fail2ban unattended-upgrades nfs-common ufw at > /dev/null
|
vim \
|
||||||
|
ca-certificates \
|
||||||
|
curl \
|
||||||
|
gnupg \
|
||||||
|
lsb-release \
|
||||||
|
fail2ban \
|
||||||
|
unattended-upgrades \
|
||||||
|
nfs-common \
|
||||||
|
ufw \
|
||||||
|
at \
|
||||||
|
git \
|
||||||
|
> /dev/null
|
||||||
|
|
||||||
|
# Step 4: Install Tailscale
|
||||||
log_info "Installing Tailscale..."
|
log_info "Installing Tailscale..."
|
||||||
curl -fsSL https://tailscale.com/install.sh | sh
|
curl -fsSL https://tailscale.com/install.sh | sh
|
||||||
|
|
||||||
log_info "Connecting to Tailscale..."
|
log_info "Connecting to Tailscale (SSH only)..."
|
||||||
sudo tailscale up --ssh
|
sudo tailscale up --ssh
|
||||||
|
|
||||||
|
# Get Tailscale hostname for display
|
||||||
|
TS_FQDN=$(tailscale status --json 2>/dev/null | awk -F'"' '
|
||||||
|
/"Self"/ { in_self=1 }
|
||||||
|
in_self && /"DNSName"/ { gsub(/\.$/, "", $4); print $4; exit }
|
||||||
|
' || echo "${HOSTNAME}.ts.net")
|
||||||
|
|
||||||
|
# Step 5: Install Docker
|
||||||
log_info "Installing Docker..."
|
log_info "Installing Docker..."
|
||||||
# Use official Docker installation method (DEB822 format)
|
|
||||||
# See: https://docs.docker.com/engine/install/debian/
|
|
||||||
sudo install -m 0755 -d /etc/apt/keyrings
|
sudo install -m 0755 -d /etc/apt/keyrings
|
||||||
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
|
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
|
||||||
sudo chmod a+r /etc/apt/keyrings/docker.asc
|
sudo chmod a+r /etc/apt/keyrings/docker.asc
|
||||||
|
|
||||||
# Add Docker repository using DEB822 format (.sources)
|
|
||||||
# shellcheck disable=SC1091
|
# shellcheck disable=SC1091
|
||||||
sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null <<EOF
|
sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null <<EOF
|
||||||
Types: deb
|
Types: deb
|
||||||
@@ -119,97 +106,83 @@ Signed-By: /etc/apt/keyrings/docker.asc
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
sudo apt-get update -qq
|
sudo apt-get update -qq
|
||||||
sudo apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin > /dev/null
|
sudo apt-get install -y -qq \
|
||||||
|
docker-ce \
|
||||||
|
docker-ce-cli \
|
||||||
|
containerd.io \
|
||||||
|
docker-buildx-plugin \
|
||||||
|
docker-compose-plugin \
|
||||||
|
> /dev/null
|
||||||
|
|
||||||
|
# Step 6: Configure Docker for user
|
||||||
log_info "Adding current user to docker group..."
|
log_info "Adding current user to docker group..."
|
||||||
sudo usermod -aG docker "$USER"
|
sudo usermod -aG docker "$USER"
|
||||||
|
|
||||||
log_info "Configuring NFS mounts..."
|
# Step 7: Configure UFW firewall
|
||||||
sudo mkdir -p "$NFS_MOUNT_DOWNLOAD" "$NFS_MOUNT_MEDIA"
|
|
||||||
|
|
||||||
# Add download share to fstab if not already present
|
|
||||||
if ! grep -q "${NFS_SERVER}:${NFS_SHARE_DOWNLOAD}" /etc/fstab; then
|
|
||||||
log_info "Adding NFS mount: ${NFS_SERVER}:${NFS_SHARE_DOWNLOAD} -> ${NFS_MOUNT_DOWNLOAD}"
|
|
||||||
echo "${NFS_SERVER}:${NFS_SHARE_DOWNLOAD} ${NFS_MOUNT_DOWNLOAD} nfs ${NFS_OPTS} 0 0" | sudo tee -a /etc/fstab > /dev/null
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Add media share to fstab if not already present
|
|
||||||
if ! grep -q "${NFS_SERVER}:${NFS_SHARE_MEDIA}" /etc/fstab; then
|
|
||||||
log_info "Adding NFS mount: ${NFS_SERVER}:${NFS_SHARE_MEDIA} -> ${NFS_MOUNT_MEDIA}"
|
|
||||||
echo "${NFS_SERVER}:${NFS_SHARE_MEDIA} ${NFS_MOUNT_MEDIA} nfs ${NFS_OPTS} 0 0" | sudo tee -a /etc/fstab > /dev/null
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Mount NFS shares
|
|
||||||
sudo mount -a || log_warn "NFS mount failed - ensure NAS is accessible via Tailscale"
|
|
||||||
|
|
||||||
log_info "Creating Transmission stack..."
|
|
||||||
mkdir -p "$TRANSMISSION_DIR"
|
|
||||||
|
|
||||||
cat > "$TRANSMISSION_DIR/docker-compose.yml" << EOF
|
|
||||||
services:
|
|
||||||
transmission:
|
|
||||||
image: linuxserver/transmission:latest
|
|
||||||
container_name: transmission
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
# Peer port - public for seeding
|
|
||||||
- "${PEER_PORT}:${PEER_PORT}"
|
|
||||||
- "${PEER_PORT}:${PEER_PORT}/udp"
|
|
||||||
# WebUI - bound to localhost only (exposed via tailscale serve)
|
|
||||||
- "127.0.0.1:9091:9091"
|
|
||||||
volumes:
|
|
||||||
- ./config:/config
|
|
||||||
# Downloads: incomplete and complete torrents
|
|
||||||
- ${NFS_MOUNT_DOWNLOAD}:/downloads
|
|
||||||
# Media: final destination for completed ISOs and images
|
|
||||||
- ${NFS_MOUNT_MEDIA}:/media
|
|
||||||
environment:
|
|
||||||
- PUID=1000
|
|
||||||
- PGID=1000
|
|
||||||
- TZ=${TIMEZONE}
|
|
||||||
- USER=${TRANSMISSION_USER}
|
|
||||||
- PASS=${TRANSMISSION_PASS}
|
|
||||||
- PEERPORT=${PEER_PORT}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
log_info "Starting Transmission..."
|
|
||||||
cd "$TRANSMISSION_DIR"
|
|
||||||
# Use sg to run docker compose with the new docker group membership
|
|
||||||
sg docker -c "docker compose up -d"
|
|
||||||
|
|
||||||
log_info "Exposing Transmission WebUI via Tailscale..."
|
|
||||||
sudo tailscale serve --bg http://localhost:9091
|
|
||||||
|
|
||||||
# Get Tailscale hostname for final message
|
|
||||||
TS_HOSTNAME=$(get_tailscale_hostname)
|
|
||||||
|
|
||||||
log_info "Configuring UFW firewall..."
|
log_info "Configuring UFW firewall..."
|
||||||
sudo ufw --force reset > /dev/null
|
sudo ufw --force reset > /dev/null
|
||||||
sudo ufw default deny incoming > /dev/null
|
sudo ufw default deny incoming > /dev/null
|
||||||
sudo ufw default allow outgoing > /dev/null
|
sudo ufw default allow outgoing > /dev/null
|
||||||
# Allow BitTorrent peer port from public internet
|
# BitTorrent peer port (public)
|
||||||
sudo ufw allow ${PEER_PORT}/tcp > /dev/null
|
sudo ufw allow 51413/tcp > /dev/null
|
||||||
sudo ufw allow ${PEER_PORT}/udp > /dev/null
|
sudo ufw allow 51413/udp > /dev/null
|
||||||
# Allow all traffic on Tailscale interface
|
# Allow all traffic on Tailscale interface
|
||||||
sudo ufw allow in on tailscale0 > /dev/null
|
sudo ufw allow in on tailscale0 > /dev/null
|
||||||
# Temporarily allow SSH during setup (safety net)
|
# Temporary SSH access (safety net)
|
||||||
sudo ufw allow 22/tcp > /dev/null
|
sudo ufw allow 22/tcp > /dev/null
|
||||||
sudo ufw --force enable > /dev/null
|
sudo ufw --force enable > /dev/null
|
||||||
|
|
||||||
# Schedule SSH rule removal in 5 minutes
|
# Schedule SSH rule removal in 5 minutes
|
||||||
log_warn "SSH port 22 temporarily open for 5 minutes (safety net)."
|
log_warn "SSH port 22 temporarily open for 5 minutes (safety net)."
|
||||||
log_warn "Verify Tailscale SSH access works, then wait or run: sudo ufw delete allow 22/tcp"
|
echo "sudo ufw delete allow 22/tcp && logger 'UFW: SSH port 22 closed'" | sudo at now + 5 minutes 2>/dev/null || {
|
||||||
echo "sudo ufw delete allow 22/tcp && logger 'UFW: SSH port 22 closed by scheduled task'" | sudo at now + 5 minutes 2>/dev/null || {
|
log_warn "Could not schedule automatic SSH cleanup. Run manually:"
|
||||||
log_warn "Could not schedule automatic SSH cleanup. Run manually after verification:"
|
|
||||||
log_warn " sudo ufw delete allow 22/tcp"
|
log_warn " sudo ufw delete allow 22/tcp"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Step 8: Create directory structure
|
||||||
|
log_info "Creating directory structure..."
|
||||||
|
sudo mkdir -p "$SEEDBOX_DIR"
|
||||||
|
sudo mkdir -p "$DOWNLOADS_DIR"
|
||||||
|
sudo chown -R "$USER:$USER" "$SEEDBOX_DIR"
|
||||||
|
|
||||||
|
# Step 9: Configure NFS mount (if NFS_SERVER provided)
|
||||||
|
if [[ -n "$NFS_SERVER" ]]; then
|
||||||
|
log_info "Configuring NFS mount..."
|
||||||
|
sudo mkdir -p "$NFS_MOUNT_MEDIA"
|
||||||
|
|
||||||
|
if ! grep -q "${NFS_SERVER}:${NFS_SHARE_MEDIA}" /etc/fstab; then
|
||||||
|
log_info "Adding NFS mount: ${NFS_SERVER}:${NFS_SHARE_MEDIA} -> ${NFS_MOUNT_MEDIA}"
|
||||||
|
echo "${NFS_SERVER}:${NFS_SHARE_MEDIA} ${NFS_MOUNT_MEDIA} nfs ${NFS_OPTS} 0 0" | sudo tee -a /etc/fstab > /dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
sudo mount -a || log_warn "NFS mount failed - ensure NAS is accessible via Tailscale"
|
||||||
|
else
|
||||||
|
log_warn "NFS_SERVER not set. NFS mount skipped. Set it later if needed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 10: Clone repository
|
||||||
|
log_info "Cloning infra-scripts repository..."
|
||||||
|
if [[ -d "${SEEDBOX_DIR}/.git" ]]; then
|
||||||
|
cd "$SEEDBOX_DIR"
|
||||||
|
git pull origin main || log_warn "Git pull failed"
|
||||||
|
else
|
||||||
|
git clone "$REPO_URL" "${SEEDBOX_DIR}/repo-tmp"
|
||||||
|
mv "${SEEDBOX_DIR}/repo-tmp/seedbox/"* "$SEEDBOX_DIR/" 2>/dev/null || true
|
||||||
|
mv "${SEEDBOX_DIR}/repo-tmp/seedbox/".* "$SEEDBOX_DIR/" 2>/dev/null || true
|
||||||
|
rm -rf "${SEEDBOX_DIR}/repo-tmp"
|
||||||
|
cd "$SEEDBOX_DIR"
|
||||||
|
git init
|
||||||
|
git remote add origin "$REPO_URL"
|
||||||
|
git fetch origin
|
||||||
|
git checkout -b main --track origin/main -- seedbox/ 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 11: Configure MOTD
|
||||||
log_info "Configuring MOTD..."
|
log_info "Configuring MOTD..."
|
||||||
sudo chmod -x /etc/update-motd.d/* 2>/dev/null || true
|
sudo chmod -x /etc/update-motd.d/* 2>/dev/null || true
|
||||||
|
|
||||||
cat << 'MOTD' | sudo tee /etc/update-motd.d/00-seedbox > /dev/null
|
cat << 'MOTD' | sudo tee /etc/update-motd.d/00-seedbox > /dev/null
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Get Tailscale FQDN from Self.DNSName
|
|
||||||
TS_FQDN=$(tailscale status --json 2>/dev/null | awk -F'"' '
|
TS_FQDN=$(tailscale status --json 2>/dev/null | awk -F'"' '
|
||||||
/"Self"/ { in_self=1 }
|
/"Self"/ { in_self=1 }
|
||||||
in_self && /"DNSName"/ { gsub(/\.$/, "", $4); print $4; exit }
|
in_self && /"DNSName"/ { gsub(/\.$/, "", $4); print $4; exit }
|
||||||
@@ -223,49 +196,56 @@ echo "\___ \| _| | _| | | | | _ \| | | \ /"
|
|||||||
echo " ___) | |___| |___| |_| | |_) | |_| / \\"
|
echo " ___) | |___| |___| |_| | |_) | |_| / \\"
|
||||||
echo "|____/|_____|_____|____/|____/ \___/_/\_\\"
|
echo "|____/|_____|_____|____/|____/ \___/_/\_\\"
|
||||||
echo ""
|
echo ""
|
||||||
echo "ISO Seedbox Server - Transmission"
|
echo "Docker Seedbox Server"
|
||||||
echo "─────────────────────────────────────────"
|
echo "─────────────────────────────────────────"
|
||||||
echo "Access:"
|
echo "Access:"
|
||||||
echo " • WebUI : https://${TS_FQDN}"
|
echo " • SSH : ${TS_FQDN}"
|
||||||
echo " • SSH : Tailscale only"
|
echo " • Seeding : Public port 51413"
|
||||||
echo " • Seeding : Public port 51413"
|
echo ""
|
||||||
|
echo "Services: (via Tailscale)"
|
||||||
|
docker ps --format ' • {{.Names}} : {{.Status}}' 2>/dev/null || echo " Docker not running"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Storage:"
|
echo "Storage:"
|
||||||
echo " • Downloads : /mnt/download"
|
echo " • Downloads : /srv/seedbox/downloads"
|
||||||
echo " • Media : /mnt/media"
|
echo " • Media : /mnt/media (NFS)"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Useful commands:"
|
echo "Useful commands:"
|
||||||
echo " cd ~/transmission && docker compose logs -f"
|
echo " cd /srv/seedbox && docker compose ls"
|
||||||
echo " df -h /mnt/download /mnt/media"
|
echo " docker logs -f <container>"
|
||||||
echo "─────────────────────────────────────────"
|
echo "─────────────────────────────────────────"
|
||||||
echo ""
|
echo ""
|
||||||
MOTD
|
MOTD
|
||||||
sudo chmod +x /etc/update-motd.d/00-seedbox
|
sudo chmod +x /etc/update-motd.d/00-seedbox
|
||||||
|
|
||||||
|
# Final summary
|
||||||
echo ""
|
echo ""
|
||||||
log_info "=========================================="
|
log_info "=========================================="
|
||||||
log_info "Deployment complete!"
|
log_info "Server setup complete!"
|
||||||
log_info "=========================================="
|
log_info "=========================================="
|
||||||
echo ""
|
echo ""
|
||||||
echo "Transmission WebUI:"
|
echo "Server accessible at:"
|
||||||
echo " URL : https://${TS_HOSTNAME}"
|
echo " SSH: ${TS_FQDN}"
|
||||||
echo " Username : ${TRANSMISSION_USER}"
|
|
||||||
echo " Password : ${TRANSMISSION_PASS}"
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Storage (NFS mounts):"
|
echo "Directory structure:"
|
||||||
echo " Downloads : ${NFS_SERVER}:${NFS_SHARE_DOWNLOAD} -> ${NFS_MOUNT_DOWNLOAD}"
|
echo " ${SEEDBOX_DIR}/"
|
||||||
echo " Media : ${NFS_SERVER}:${NFS_SHARE_MEDIA} -> ${NFS_MOUNT_MEDIA}"
|
echo " ├── stacks/ # Docker Compose stacks"
|
||||||
|
echo " ├── downloads/ # Local downloads (SSD)"
|
||||||
|
echo " └── .env # Secrets (created by Gitea Actions)"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Transmission paths:"
|
echo "NFS mount:"
|
||||||
echo " /downloads -> ${NFS_MOUNT_DOWNLOAD} (incomplete + complete torrents)"
|
if [[ -n "$NFS_SERVER" ]]; then
|
||||||
echo " /media -> ${NFS_MOUNT_MEDIA} (final destination for ISOs)"
|
echo " ${NFS_SERVER}:${NFS_SHARE_MEDIA} -> ${NFS_MOUNT_MEDIA}"
|
||||||
|
else
|
||||||
|
echo " Not configured. Run with NFS_SERVER=<ip> to configure."
|
||||||
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
echo "Peer port : ${PEER_PORT} (public)"
|
echo "Next steps:"
|
||||||
|
echo " 1. Configure Gitea secrets (see README.md)"
|
||||||
|
echo " 2. Push to main branch to trigger deployment"
|
||||||
|
echo " 3. Services will be available at <service>.taila5ad8.ts.net"
|
||||||
echo ""
|
echo ""
|
||||||
log_warn "SSH port 22 will be closed in 5 minutes."
|
log_warn "SSH port 22 will be closed in 5 minutes."
|
||||||
log_warn "To cancel: sudo atq (list jobs) then sudo atrm <job-number>"
|
log_warn "Use Tailscale SSH: ssh ${TS_FQDN}"
|
||||||
echo ""
|
|
||||||
echo "Save these credentials! The password was auto-generated."
|
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
52
seedbox/stacks/portainer/README.md
Normal file
52
seedbox/stacks/portainer/README.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# Portainer Stack
|
||||||
|
|
||||||
|
Docker management UI, accessible via Tailscale.
|
||||||
|
|
||||||
|
## Access
|
||||||
|
|
||||||
|
- **URL**: https://portainer.taila5ad8.ts.net
|
||||||
|
- **Initial setup**: Create admin account on first access
|
||||||
|
|
||||||
|
## Ports
|
||||||
|
|
||||||
|
| Port | Protocol | Exposure | Description |
|
||||||
|
|------|----------|----------|-------------|
|
||||||
|
| 9000 | TCP | Tailscale only | WebUI |
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- View and manage all Docker containers
|
||||||
|
- View logs in real-time
|
||||||
|
- Execute commands in containers
|
||||||
|
- Manage Docker networks and volumes
|
||||||
|
- Deploy stacks from templates
|
||||||
|
|
||||||
|
## First Setup
|
||||||
|
|
||||||
|
1. Access https://portainer.taila5ad8.ts.net
|
||||||
|
2. Create an admin account
|
||||||
|
3. Select "Docker" as environment type
|
||||||
|
4. Connect to local Docker socket
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check logs
|
||||||
|
docker logs portainer
|
||||||
|
|
||||||
|
# Check Tailscale sidecar
|
||||||
|
docker exec ts-portainer tailscale status
|
||||||
|
|
||||||
|
# Restart stack
|
||||||
|
cd /srv/seedbox/stacks/portainer
|
||||||
|
docker compose restart
|
||||||
|
```
|
||||||
|
|
||||||
|
## Note
|
||||||
|
|
||||||
|
Portainer is optional and mainly useful for:
|
||||||
|
- Visual monitoring of containers
|
||||||
|
- Quick access to logs
|
||||||
|
- Emergency container management
|
||||||
|
|
||||||
|
All deployments should still go through Gitea Actions for proper version control.
|
||||||
33
seedbox/stacks/portainer/docker-compose.yml
Normal file
33
seedbox/stacks/portainer/docker-compose.yml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
services:
|
||||||
|
ts-portainer:
|
||||||
|
image: tailscale/tailscale:latest
|
||||||
|
hostname: portainer
|
||||||
|
environment:
|
||||||
|
- TS_AUTHKEY=${TS_AUTHKEY}
|
||||||
|
- TS_EXTRA_ARGS=--advertise-tags=tag:container
|
||||||
|
- TS_STATE_DIR=/var/lib/tailscale
|
||||||
|
- TS_SERVE_CONFIG=/config/serve.json
|
||||||
|
- TS_USERSPACE=false
|
||||||
|
volumes:
|
||||||
|
- ts-state:/var/lib/tailscale
|
||||||
|
- ./serve.json:/config/serve.json:ro
|
||||||
|
devices:
|
||||||
|
- /dev/net/tun:/dev/net/tun
|
||||||
|
cap_add:
|
||||||
|
- net_admin
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
portainer:
|
||||||
|
image: portainer/portainer-ce:latest
|
||||||
|
container_name: portainer
|
||||||
|
network_mode: service:ts-portainer
|
||||||
|
depends_on:
|
||||||
|
- ts-portainer
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- data:/data
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
ts-state:
|
||||||
|
data:
|
||||||
16
seedbox/stacks/portainer/serve.json
Normal file
16
seedbox/stacks/portainer/serve.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"TCP": {
|
||||||
|
"443": {
|
||||||
|
"HTTPS": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Web": {
|
||||||
|
"portainer.taila5ad8.ts.net:443": {
|
||||||
|
"Handlers": {
|
||||||
|
"/": {
|
||||||
|
"Proxy": "http://127.0.0.1:9000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
61
seedbox/stacks/prowlarr/README.md
Normal file
61
seedbox/stacks/prowlarr/README.md
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# Prowlarr Stack
|
||||||
|
|
||||||
|
Indexer manager for Sonarr, Radarr, and other *arr applications.
|
||||||
|
|
||||||
|
## Access
|
||||||
|
|
||||||
|
- **URL**: https://prowlarr.taila5ad8.ts.net
|
||||||
|
- **Initial setup**: Configure on first access (no default credentials)
|
||||||
|
|
||||||
|
## Ports
|
||||||
|
|
||||||
|
| Port | Protocol | Exposure | Description |
|
||||||
|
|------|----------|----------|-------------|
|
||||||
|
| 9696 | TCP | Tailscale only | WebUI |
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Centralized indexer management
|
||||||
|
- Automatic sync with Sonarr/Radarr
|
||||||
|
- Support for Usenet and Torrent indexers
|
||||||
|
- Built-in indexer testing
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Connect to Sonarr
|
||||||
|
|
||||||
|
1. In Prowlarr: **Settings → Apps → Add**
|
||||||
|
2. Select "Sonarr"
|
||||||
|
3. Prowlarr Server: `http://localhost:9696` (or use Tailscale hostname)
|
||||||
|
4. Sonarr Server: `http://sonarr.taila5ad8.ts.net` or internal Docker network
|
||||||
|
5. API Key: Get from Sonarr **Settings → General**
|
||||||
|
|
||||||
|
### Add Indexers
|
||||||
|
|
||||||
|
1. Go to **Indexers → Add**
|
||||||
|
2. Search for your preferred indexer
|
||||||
|
3. Configure credentials if required
|
||||||
|
4. Test and save
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check logs
|
||||||
|
docker logs prowlarr
|
||||||
|
|
||||||
|
# Check Tailscale sidecar
|
||||||
|
docker exec ts-prowlarr tailscale status
|
||||||
|
|
||||||
|
# Restart stack
|
||||||
|
cd /srv/seedbox/stacks/prowlarr
|
||||||
|
docker compose restart
|
||||||
|
|
||||||
|
# Check WebUI is responding
|
||||||
|
docker exec ts-prowlarr curl -s http://127.0.0.1:9696
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integration Notes
|
||||||
|
|
||||||
|
Prowlarr can communicate with other *arr apps via:
|
||||||
|
1. **Tailscale hostnames** (e.g., `sonarr.taila5ad8.ts.net`) - Recommended
|
||||||
|
2. **Docker network** - Requires additional network configuration
|
||||||
36
seedbox/stacks/prowlarr/docker-compose.yml
Normal file
36
seedbox/stacks/prowlarr/docker-compose.yml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
services:
|
||||||
|
ts-prowlarr:
|
||||||
|
image: tailscale/tailscale:latest
|
||||||
|
hostname: prowlarr
|
||||||
|
environment:
|
||||||
|
- TS_AUTHKEY=${TS_AUTHKEY}
|
||||||
|
- TS_EXTRA_ARGS=--advertise-tags=tag:container
|
||||||
|
- TS_STATE_DIR=/var/lib/tailscale
|
||||||
|
- TS_SERVE_CONFIG=/config/serve.json
|
||||||
|
- TS_USERSPACE=false
|
||||||
|
volumes:
|
||||||
|
- ts-state:/var/lib/tailscale
|
||||||
|
- ./serve.json:/config/serve.json:ro
|
||||||
|
devices:
|
||||||
|
- /dev/net/tun:/dev/net/tun
|
||||||
|
cap_add:
|
||||||
|
- net_admin
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
prowlarr:
|
||||||
|
image: linuxserver/prowlarr:latest
|
||||||
|
container_name: prowlarr
|
||||||
|
network_mode: service:ts-prowlarr
|
||||||
|
depends_on:
|
||||||
|
- ts-prowlarr
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Europe/Paris
|
||||||
|
volumes:
|
||||||
|
- config:/config
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
ts-state:
|
||||||
|
config:
|
||||||
16
seedbox/stacks/prowlarr/serve.json
Normal file
16
seedbox/stacks/prowlarr/serve.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"TCP": {
|
||||||
|
"443": {
|
||||||
|
"HTTPS": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Web": {
|
||||||
|
"prowlarr.taila5ad8.ts.net:443": {
|
||||||
|
"Handlers": {
|
||||||
|
"/": {
|
||||||
|
"Proxy": "http://127.0.0.1:9696"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
82
seedbox/stacks/sonarr/README.md
Normal file
82
seedbox/stacks/sonarr/README.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# Sonarr Stack
|
||||||
|
|
||||||
|
TV series management and automation.
|
||||||
|
|
||||||
|
## Access
|
||||||
|
|
||||||
|
- **URL**: https://sonarr.taila5ad8.ts.net
|
||||||
|
- **Initial setup**: Configure on first access (no default credentials)
|
||||||
|
|
||||||
|
## Ports
|
||||||
|
|
||||||
|
| Port | Protocol | Exposure | Description |
|
||||||
|
|------|----------|----------|-------------|
|
||||||
|
| 8989 | TCP | Tailscale only | WebUI |
|
||||||
|
|
||||||
|
## Volumes
|
||||||
|
|
||||||
|
| Path in container | Host path | Description |
|
||||||
|
|-------------------|-----------|-------------|
|
||||||
|
| `/config` | Docker volume | Sonarr configuration |
|
||||||
|
| `/downloads` | `/srv/seedbox/downloads` | Download directory |
|
||||||
|
| `/media` | `/mnt/media` | Media library (NFS) |
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Root Folders
|
||||||
|
|
||||||
|
1. Go to **Settings → Media Management → Root Folders**
|
||||||
|
2. Add `/media/TV` or your preferred path
|
||||||
|
|
||||||
|
### Download Client
|
||||||
|
|
||||||
|
1. Go to **Settings → Download Clients → Add**
|
||||||
|
2. Select "Transmission"
|
||||||
|
3. Host: `transmission.taila5ad8.ts.net`
|
||||||
|
4. Port: `443`
|
||||||
|
5. Use SSL: ✅
|
||||||
|
6. Username/Password: Your Transmission credentials
|
||||||
|
|
||||||
|
### Connect to Prowlarr
|
||||||
|
|
||||||
|
1. Go to **Settings → Indexers**
|
||||||
|
2. Prowlarr will auto-sync indexers if configured correctly
|
||||||
|
|
||||||
|
### Quality Profiles
|
||||||
|
|
||||||
|
Configure in **Settings → Profiles** based on your preferences.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check logs
|
||||||
|
docker logs sonarr
|
||||||
|
|
||||||
|
# Check Tailscale sidecar
|
||||||
|
docker exec ts-sonarr tailscale status
|
||||||
|
|
||||||
|
# Restart stack
|
||||||
|
cd /srv/seedbox/stacks/sonarr
|
||||||
|
docker compose restart
|
||||||
|
|
||||||
|
# Check WebUI is responding
|
||||||
|
docker exec ts-sonarr curl -s http://127.0.0.1:8989
|
||||||
|
```
|
||||||
|
|
||||||
|
## Recommended Folder Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
/mnt/media/
|
||||||
|
├── TV/
|
||||||
|
│ ├── Show Name (Year)/
|
||||||
|
│ │ ├── Season 01/
|
||||||
|
│ │ └── Season 02/
|
||||||
|
│ └── Another Show/
|
||||||
|
└── downloads/
|
||||||
|
└── completed/
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Key
|
||||||
|
|
||||||
|
Find your API key in **Settings → General → Security**.
|
||||||
|
This is needed for Prowlarr integration.
|
||||||
38
seedbox/stacks/sonarr/docker-compose.yml
Normal file
38
seedbox/stacks/sonarr/docker-compose.yml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
services:
|
||||||
|
ts-sonarr:
|
||||||
|
image: tailscale/tailscale:latest
|
||||||
|
hostname: sonarr
|
||||||
|
environment:
|
||||||
|
- TS_AUTHKEY=${TS_AUTHKEY}
|
||||||
|
- TS_EXTRA_ARGS=--advertise-tags=tag:container
|
||||||
|
- TS_STATE_DIR=/var/lib/tailscale
|
||||||
|
- TS_SERVE_CONFIG=/config/serve.json
|
||||||
|
- TS_USERSPACE=false
|
||||||
|
volumes:
|
||||||
|
- ts-state:/var/lib/tailscale
|
||||||
|
- ./serve.json:/config/serve.json:ro
|
||||||
|
devices:
|
||||||
|
- /dev/net/tun:/dev/net/tun
|
||||||
|
cap_add:
|
||||||
|
- net_admin
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
sonarr:
|
||||||
|
image: linuxserver/sonarr:latest
|
||||||
|
container_name: sonarr
|
||||||
|
network_mode: service:ts-sonarr
|
||||||
|
depends_on:
|
||||||
|
- ts-sonarr
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Europe/Paris
|
||||||
|
volumes:
|
||||||
|
- config:/config
|
||||||
|
- /srv/seedbox/downloads:/downloads
|
||||||
|
- /mnt/media:/media
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
ts-state:
|
||||||
|
config:
|
||||||
52
seedbox/stacks/transmission/README.md
Normal file
52
seedbox/stacks/transmission/README.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# Transmission Stack
|
||||||
|
|
||||||
|
BitTorrent client with web interface, accessible via Tailscale.
|
||||||
|
|
||||||
|
## Access
|
||||||
|
|
||||||
|
- **URL**: https://transmission.taila5ad8.ts.net
|
||||||
|
- **Default credentials**: Set via `TRANSMISSION_USER` and `TRANSMISSION_PASS` secrets
|
||||||
|
|
||||||
|
## Ports
|
||||||
|
|
||||||
|
| Port | Protocol | Exposure | Description |
|
||||||
|
|------|----------|----------|-------------|
|
||||||
|
| 9091 | TCP | Tailscale only | WebUI |
|
||||||
|
| 51413 | TCP/UDP | Public | BitTorrent peer port |
|
||||||
|
|
||||||
|
## Volumes
|
||||||
|
|
||||||
|
| Path in container | Host path | Description |
|
||||||
|
|-------------------|-----------|-------------|
|
||||||
|
| `/config` | Docker volume | Transmission configuration |
|
||||||
|
| `/downloads` | `/srv/seedbox/downloads` | Download directory (local SSD) |
|
||||||
|
| `/media` | `/mnt/media` | Media library (NFS) |
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Download Paths
|
||||||
|
|
||||||
|
In Transmission settings:
|
||||||
|
- **Download to**: `/downloads`
|
||||||
|
- **Move completed to**: `/media/downloads` (optional)
|
||||||
|
|
||||||
|
### Peer Port
|
||||||
|
|
||||||
|
The peer port 51413 is exposed publicly for optimal seeding performance. Ensure your firewall/router allows this port.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check logs
|
||||||
|
docker logs transmission
|
||||||
|
|
||||||
|
# Check Tailscale sidecar
|
||||||
|
docker exec ts-transmission tailscale status
|
||||||
|
|
||||||
|
# Restart stack
|
||||||
|
cd /srv/seedbox/stacks/transmission
|
||||||
|
docker compose restart
|
||||||
|
|
||||||
|
# Check WebUI is responding
|
||||||
|
docker exec ts-transmission curl -s http://127.0.0.1:9091
|
||||||
|
```
|
||||||
45
seedbox/stacks/transmission/docker-compose.yml
Normal file
45
seedbox/stacks/transmission/docker-compose.yml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
services:
|
||||||
|
ts-transmission:
|
||||||
|
image: tailscale/tailscale:latest
|
||||||
|
hostname: transmission
|
||||||
|
environment:
|
||||||
|
- TS_AUTHKEY=${TS_AUTHKEY}
|
||||||
|
- TS_EXTRA_ARGS=--advertise-tags=tag:container
|
||||||
|
- TS_STATE_DIR=/var/lib/tailscale
|
||||||
|
- TS_SERVE_CONFIG=/config/serve.json
|
||||||
|
- TS_USERSPACE=false
|
||||||
|
volumes:
|
||||||
|
- ts-state:/var/lib/tailscale
|
||||||
|
- ./serve.json:/config/serve.json:ro
|
||||||
|
devices:
|
||||||
|
- /dev/net/tun:/dev/net/tun
|
||||||
|
cap_add:
|
||||||
|
- net_admin
|
||||||
|
ports:
|
||||||
|
# BitTorrent peer port - exposed publicly for seeding
|
||||||
|
- "51413:51413"
|
||||||
|
- "51413:51413/udp"
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
transmission:
|
||||||
|
image: linuxserver/transmission:latest
|
||||||
|
container_name: transmission
|
||||||
|
network_mode: service:ts-transmission
|
||||||
|
depends_on:
|
||||||
|
- ts-transmission
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=Europe/Paris
|
||||||
|
- USER=${TRANSMISSION_USER:-admin}
|
||||||
|
- PASS=${TRANSMISSION_PASS}
|
||||||
|
- PEERPORT=51413
|
||||||
|
volumes:
|
||||||
|
- config:/config
|
||||||
|
- /srv/seedbox/downloads:/downloads
|
||||||
|
- /mnt/media:/media
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
ts-state:
|
||||||
|
config:
|
||||||
16
seedbox/stacks/transmission/serve.json
Normal file
16
seedbox/stacks/transmission/serve.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"TCP": {
|
||||||
|
"443": {
|
||||||
|
"HTTPS": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Web": {
|
||||||
|
"transmission.taila5ad8.ts.net:443": {
|
||||||
|
"Handlers": {
|
||||||
|
"/": {
|
||||||
|
"Proxy": "http://127.0.0.1:9091"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user