Move homelab to runit

This commit is contained in:
Neale Pickett 2023-12-28 10:07:43 -07:00
parent c2f2fcc38b
commit 5400171298
80 changed files with 523 additions and 1435 deletions

3
homelab/.gitignore vendored
View File

@ -1,3 +0,0 @@
password
samba-users.env
secrets

View File

@ -1,19 +1,3 @@
# Neale's Homelab
My homelab now uses runit-managed podman containers.
This is the stuff I run on my little Raspberry Pi.
I guess I fiddle around with it pretty frequently.
## Portal
In the [www](www) directory is a static HTML/JavaScript portal thing I wrote.
It doesn't need any server configuration,
you just edit the HTML to manage what apps it serves.
Other things like this exist,
but they all require ridiculous things like relational databases or weird config files.
I should probably package this up or something,
but then I'd have to put effort into publicizing it,
and run a ticketing system or something,
so meh.
runit essentially runs `*/run` in an endless loop.

View File

@ -1,9 +0,0 @@
* Single Sign-On
* [x] Replace simpleauth with somebody else's project
* [x] Set up Forgejo OIDC to Authelia (there's a guide on Authelia's site)
* [x] Persist "remember me" across reboots
* LDAP restrictions
* [x] People can only r/w their own storage
* [x] Public storage
* [x] Per-Group storage
* [x] Media-Sucker secure setup (bind to 0.0.0.0 opens to internet)

19
homelab/atlas/run Executable file
View File

@ -0,0 +1,19 @@
#! /bin/sh
me=$(basename $(pwd))
image=jamesits/ripe-atlas:latest
podman pull $image
podman rm $me
podman run \
--name $me \
--cpu-shares 128 \
--cap-drop ALL \
--cap-add CHOWN \
--cap-add SETUID \
--cap-add SETGID \
--cap-add DAC_OVERRIDE \
--cap-add NET_RAW \
--volume /srv/sys/atlas/etc:/var/atlas-probe/etc \
--volume /srv/sys/atlas/status:/var/atlas-probe/status \
$image

View File

@ -0,0 +1,3 @@
melville.woozle.org ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC6fMdw0m8wvh5+9IUcNgjxk0TgjoTT1anESnsYWIcnmS8l7PzkB/WfCEZEU0QzKvKXSp37jd30iR5U9jM7zTjYrjCJZvK0dxShC9iIgR5akAThXvlH0KkHVAvhmQda6nvMWQugp359Usp4zH90/J7G//+3HEQ9zfXW+p29bwBOdyDHfjDSIz95ysy8ivwMFKuPgAsNK4FDfBsGVHCM5xu5bn0dSaHeqGn7bneY00zwQqQot6rOw1Pao2tEY04Vu4KC1VKVWKU38dNBEFTOQBxYhiVMsxY3CIwDbEQLA5gjRnYog2KvsDP9XK4W2k1Yc9PvwoinqzcgmDS7isQ8HAoZiY06CChg3hHYXRre+TcO/UrjhzrWbxdCo7XFKBiC74ZU/+t2IkBmmHEnaTpmaLazxZD75wUH9rrWwgPVFEWNp41nj0VfQfR032MwJ2iNbvkCW/2kUWe++r56TlYQjxL8FVICVBbt6pZprMbEkQm3/iPR6ibfNNa620Kz3QRUsxM=
melville.woozle.org ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBE1D4QQxjnNgmI7wteSAnAqArKQ6UTnTKmTitnU40ISizAK1j8OqXxEv0hbssNrTVyqlmLUoIeRuHd5bHGC8dEE=
melville.woozle.org ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEQKIumG2Wl3papG4lcIB9+ZEe76uMbfpP+XSlUBjR0E

View File

@ -0,0 +1,86 @@
# Example configuration file, it's safe to copy this as the default config file without any modification.
# You don't have to copy this file to your instance,
# just run `./act_runner generate-config > config.yaml` to generate a config file.
log:
# The level of logging, can be trace, debug, info, warn, error, fatal
level: info
runner:
# Where to store the registration result.
file: .runner
# Execute how many tasks concurrently at the same time.
capacity: 1
# Extra environment variables to run jobs.
envs:
A_TEST_ENV_NAME_1: a_test_env_value_1
A_TEST_ENV_NAME_2: a_test_env_value_2
# Extra environment variables to run jobs from a file.
# It will be ignored if it's empty or the file doesn't exist.
env_file: .env
# The timeout for a job to be finished.
# Please note that the Gitea instance also has a timeout (3h by default) for the job.
# So the job could be stopped by the Gitea instance if it's timeout is shorter than this.
timeout: 30m
# Whether skip verifying the TLS certificate of the Gitea instance.
insecure: false
# The timeout for fetching the job from the Gitea instance.
fetch_timeout: 5s
# The interval for fetching the job from the Gitea instance.
fetch_interval: 28s
# The labels of a runner are used to determine which jobs the runner can run, and how to run them.
labels: ["aarch64", "big-builder", "hugo", "python3", "go"]
cache:
# Enable cache server to use actions/cache.
enabled: true
# The directory to store the cache data.
# If it's empty, the cache data will be stored in $HOME/.cache/actcache.
dir: ""
# The host of the cache server.
# It's not for the address to listen, but the address to connect from job containers.
# So 0.0.0.0 is a bad choice, leave it empty to detect automatically.
host: ""
# The port of the cache server.
# 0 means to use a random available port.
port: 0
# The external cache server URL. Valid only when enable is true.
# If it's specified, act_runner will use this URL as the ACTIONS_CACHE_URL rather than start a server by itself.
# The URL should generally end with "/".
external_server: ""
container:
# Specifies the network to which the container will connect.
# Could be host, bridge or the name of a custom network.
# If it's empty, act_runner will create a network automatically.
network: ""
# Whether to use privileged mode or not when launching task containers (privileged mode is required for Docker-in-Docker).
privileged: false
# And other options to be used when the container is started (eg, --add-host=my.gitea.url:host-gateway).
options:
# The parent directory of a job's working directory.
# If it's empty, /workspace will be used.
workdir_parent:
# Volumes (including bind mounts) can be mounted to containers. Glob syntax is supported, see https://github.com/gobwas/glob
# You can specify multiple volumes. If the sequence is empty, no volumes can be mounted.
# For example, if you only allow containers to mount the `data` volume and all the json files in `/src`, you should change the config to:
# valid_volumes:
# - data
# - /src/*.json
# If you want to allow any volume, please use the following configuration:
# valid_volumes:
# - '**'
valid_volumes: []
# overrides the docker client host with the specified one.
# If it's empty, act_runner will find an available docker host automatically.
# If it's "-", act_runner will find an available docker host automatically, but the docker host won't be mounted to the job containers and service containers.
# If it's not empty or "-", the specified docker host will be used. An error will be returned if it doesn't work.
docker_host: ""
# Pull docker image(s) even if already present
force_pull: false
host:
# The parent directory of a job's working directory.
# If it's empty, $HOME/.cache/act/ will be used.
workdir_parent:

3
homelab/builder-betty/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

9
homelab/builder-betty/run Executable file
View File

@ -0,0 +1,9 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name $me \
--volume ./app/config.yaml:/app/config.yaml:ro \
--volume ./app/.ssh:/app/.ssh:ro \
--volume /srv/secrets/builder-betty/.runner:/app/.runner:ro \
git.woozle.org/neale/big-builder:1.0 -c config.yaml daemon

View File

@ -1,5 +1,5 @@
{
email neale@woozle.org
email neale@woozle.org
#debug
}
@ -21,10 +21,6 @@ drive.woozle.org {
#reverse_proxy filebrowser:80
}
media.woozle.org {
reverse_proxy jellyfin:8096
}
# XXX: have this use caddy auth
ancestry.woozle.org {
reverse_proxy geneweb:2317
@ -77,7 +73,7 @@ deergrove.woozle.org {
}
handle_path /sucker/* {
reverse_proxy host.lan:5801
reverse_proxy sucker:8080
}
handle_path /netdata/* {

3
homelab/caddy/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

13
homelab/caddy/run Executable file
View File

@ -0,0 +1,13 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name $me \
--network homelab \
--publish 80:80 \
--publish 443:443 \
--volume /srv:/srv:ro \
--volume /srv/sys/caddy:/data/caddy \
--volume ./Caddyfile:/etc/caddy/Caddyfile:ro \
--add-host host.lan:192.168.86.2 \
caddy:2-alpine

View File

@ -4,5 +4,5 @@
192.168.86.2 sweetums.woozle.org deergrove.woozle.org drive.woozle.org git.woozle.org ancestry.woozle.org media.woozle.org photos.woozle.org auth.woozle.org xfer.woozle.org
fallthrough
}
forward . 8.8.8.8
forward . 1.1.1.1 1.0.0.1 8.8.8.8
}

3
homelab/coredns/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

8
homelab/coredns/run Executable file
View File

@ -0,0 +1,8 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name $me \
--network host \
--mount type=bind,src=$(pwd)/Corefile,dst=/home/nonroot/Corefile,readonly=true \
coredns/coredns

View File

@ -1,4 +0,0 @@
address: "0.0.0.0"
port: "8000"
dir: "/data"
prefix: "/"

3
homelab/ddns/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

9
homelab/ddns/run Executable file
View File

@ -0,0 +1,9 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name $me \
--network homelab \
--dns 1.1.1.1 \
--volume /srv/sys/ddns-updater:/updater/data \
qmcgaw/ddns-updater

View File

@ -1,12 +0,0 @@
#! /bin/sh
stack=$(basename $(pwd))
extra="--resolve-image changed"
if [ "$1" = "--slow" ]; then
extra=
shift
fi
docker --context deergrove stack deploy -c docker-compose.yaml --prune $extra "$@" $stack
#docker --context deergrove compose up --detach

View File

@ -1,406 +0,0 @@
version: "3.8"
services:
coredns:
image: coredns/coredns
networks:
- hostnet
command:
- -conf
- /Corefile
configs:
- source: Corefile
target: /Corefile
caddy:
image: caddy:2-alpine
ports:
- target: 443
published: 443
mode: host
- target: 80
published: 80
mode: host
volumes:
- type: bind
source: /srv
target: /srv
read_only: true
- type: bind
source: /srv/sys/caddy
target: /data/caddy
configs:
- source: Caddyfile
target: /etc/caddy/Caddyfile
extra_hosts:
- host.docker.internal:host-gateway
- host.lan:192.168.86.2
simpleauth:
image: git.woozle.org/neale/simpleauth
secrets:
- passwd
- simpleauth.key
portal:
image: git.woozle.org/neale/portal
configs:
- source: portal.json
target: /web/portal.json
- source: deergrove.png
target: /web/portal.png
jellyfin:
image: jellyfin/jellyfin
deploy:
replicas: 0
environment:
TZ: US/Mountain
volumes:
- type: bind
source: /srv/sys/jellyfin/config
target: /config
- type: bind
source: /srv/sys/jellyfin/cache
target: /cache
- type: bind
source: /srv/media/
target: /srv/media/
read_only: true
plex:
image: lscr.io/linuxserver/plex:latest
networks:
- hostnet
environment:
TZ: US/Mountain
VERSION: public
volumes:
- type: bind
source: /srv/sys/plex
target: /config
- type: bind
source: /srv/media/
target: /srv/media/
read_only: true
pigallery2:
image: bpatrik/pigallery2:latest
volumes:
- type: bind
source: /srv/sys/pigallery2/config
target: /app/data/config
- type: bind
source: /srv/sys/pigallery2/db
target: /app/data/db
- type: bind
source: /srv/sys/pigallery2/cache
target: /app/data/cache
- type: bind
source: /srv/storage/fam/Photos
target: /photos/fam
read_only: true
- type: bind
source: /srv/storage/ginnie/Photos
target: /photos/ginnie
read_only: true
transmission:
image: lscr.io/linuxserver/transmission:latest
volumes:
- type: bind
source: /srv/sys/transmission
target: /config
- type: bind
source: /srv/incoming
target: /srv/incoming
environment:
PEERPORT: "51413"
ports:
- 51413:51413
- 51413:51413/udp
sonarr:
image: lscr.io/linuxserver/sonarr
extra_hosts:
- host.docker.internal:host-gateway
volumes:
- type: bind
source: /srv/sys/sonarr
target: /config
- type: bind
source: /srv/media/tv
target: /srv/media/tv
- type: bind
source: /srv/incoming
target: /srv/incoming
radarr:
image: lscr.io/linuxserver/radarr
extra_hosts:
- host.docker.internal:host-gateway
volumes:
- type: bind
source: /srv/sys/radarr
target: /config
- type: bind
source: /srv/media/movies
target: /srv/media/movies
- type: bind
source: /srv/incoming
target: /srv/incoming
lidarr:
image: lscr.io/linuxserver/lidarr
extra_hosts:
- host.docker.internal:host-gateway
volumes:
- type: bind
source: /srv/sys/lidarr
target: /config
- type: bind
source: /srv/media/music
target: /srv/media/music
- type: bind
source: /srv/incoming
target: /srv/incoming
readarr:
image: lscr.io/linuxserver/readarr:develop
extra_hosts:
- host.docker.internal:host-gateway
volumes:
- type: bind
source: /srv/sys/readarr
target: /config
- type: bind
source: /srv/media/books
target: /srv/media/books
- type: bind
source: /srv/media/audiobooks
target: /srv/media/audiobooks
- type: bind
source: /srv/incoming
target: /srv/incoming
prowlarr:
image: lscr.io/linuxserver/prowlarr:latest
extra_hosts:
- host.docker.internal:host-gateway
volumes:
- type: bind
source: /srv/sys/prowlarr
target: /config
nzbget:
image: lscr.io/linuxserver/nzbget
volumes:
- type: bind
source: /srv/sys/nzbget
target: /config
- type: bind
source: /srv/incoming
target: /srv/incoming
gitea:
image: codeberg.org/forgejo/forgejo:1.20.5-0-rootless
configs:
- source: gitea-robots.txt
target: /var/lib/gitea/custom/robots.txt
- source: timezone
target: /etc/timezone
- source: localtime
target: /etc/localtime
volumes:
- type: bind
source: /srv/sys/gitea/app.ini
target: /etc/gitea/app.ini
- type: bind
source: /srv/sys/gitea
target: /data
atlas:
image: ctassisf/ripe-atlas-alpine:arm64v8
volumes:
- type: bind
source: /srv/sys/atlas/etc
target: /var/atlas-probe/etc
- type: bind
source: /srv/sys/atlas/status
target: /var/atlas-probe/status
netdata:
image: netdata/netdata
hostname: "{{.Node.Hostname}}"
deploy:
replicas: 0
environment:
NETDATA_DISABLE_CLOUD: "1"
cap_add:
- SYS_PTRACE
volumes:
- type: bind
source: /
target: /host
read_only: true
- type: bind
source: /srv/sys/netdata/lib
target: /var/lib/netdata
- type: bind
source: /srv/sys/netdata/cache
target: /var/cache/netdata
configs:
- source: netdata.conf
target: /etc/netdata/netdata.conf
geneweb:
image: ravermeister/geneweb
volumes:
- type: bind
source: /srv/sys/geneweb/
target: /usr/local/share/geneweb/share/data
samba:
image: dperson/samba
volumes:
- type: bind
source: /srv
target: /srv
environment:
NMBD: enable
RECYCLE: disable
USERID: 911
GROUPID: 911
# name;path;browse;readonly;guest
SHARE1: drive;/srv;yes;no;no
SHARE2: retropie;/srv/media/games/retropie;yes;yes;yes
env_file:
- secrets/samba-users.env
ports:
- published: 139
target: 139
- published: 445
target: 445
webfs:
image: sigoden/dufs
volumes:
- type: bind
source: /srv/storage
target: /srv/storage
- type: bind
source: /srv/incoming
target: /srv/incoming
- type: bind
source: /srv/media
target: /srv/media
command:
- -A
- /srv
user: "911:911"
picoshare:
image: git.woozle.org/neale/picoshare
volumes:
- type: bind
source: /srv/sys/picoshare
target: /data
environment:
PS_SHARED_SECRET_FILE: /run/secrets/picoshare
secrets:
- picoshare
ddns:
image: qmcgaw/ddns-updater
dns:
- 1.1.1.1
volumes:
- type: bind
source: /srv/sys/ddns-updater
target: /updater/data
filebrowser:
image: filebrowser/filebrowser
volumes:
- type: bind
source: /srv/sys/filebrowser/database
target: /database
- type: bind
source: /srv/storage
target: /srv/storage
- type: bind
source: /srv/media
target: /srv/media
- type: bind
source: /srv/incoming
target: /srv/incoming
user: "911:911"
configs:
- source: filebrowser.json
target: /.filebrowser.json
configs:
dave.yaml:
file: dave.yaml
name: dave.yaml-v3
Corefile:
file: Corefile
name: Corefile-v11
Caddyfile:
file: Caddyfile
name: Caddyfile-v153
portal.json:
file: portal.json
name: portal.json-v7
deergrove.png:
file: www/deergrove.png
name: deergrove.png-v1
netdata.conf:
file: netdata.conf
name: netdata.conf-v1
gitea-robots.txt:
file: gitea-robots.txt
name: gitea-robots.txt-v1
filebrowser.json:
file: filebrowser.json
name: filebrowser.json-v2
localtime:
file: localtime
name: localtime-v1
timezone:
file: timezone
name: timezone-v1
secrets:
passwd:
file: secrets/passwd
name: passwd-v2
simpleauth.key:
file: secrets/simpleauth.key
name: simpleauth.key-v1
tunnel:
file: secrets/tunnel
name: tunnel-v1
known_hosts:
file: secrets/known_hosts
name: known_hosts-v1
jwt.secret:
file: secrets/jwt.secret
name: jwt.secret-v1
storage.secret:
file: secrets/storage.secret
name: storage.secret-v1
session.secret:
file: secrets/session.secret
name: session.secret-v1
users.yaml:
file: secrets/users.yaml
name: users.yaml-v9
authelia.oidc.yaml:
file: secrets/authelia.oidc.yaml
name: authelia.oidc.yaml-v2
picoshare:
file: secrets/picoshare
name: picoshare-v1
networks:
hostnet:
external: true
name: host

View File

@ -1,8 +0,0 @@
{
"port": 80,
"baseURL": "/",
"address": "",
"log": "stdout",
"database": "/database/filebrowser.db",
"root": "/srv"
}

3
homelab/geneweb/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

8
homelab/geneweb/run Executable file
View File

@ -0,0 +1,8 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name $me \
--network homelab \
--volume /srv/sys/geneweb:/usr/local/share/geneweb/share/data \
ravermeister/geneweb

3
homelab/gitea/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

12
homelab/gitea/run Executable file
View File

@ -0,0 +1,12 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name $me \
--network homelab \
--volume ./gitea-robots.txt:/var/lib/gitea/custom/robots.txt:ro \
--volume ./timezone:/etc/timezone:ro \
--volume /etc/localtime:/etc/localtime:ro \
--volume /srv/sys/gitea/app.ini:/etc/gitea/app.ini \
--volume /srv/sys/gitea:/data \
codeberg.org/forgejo/forgejo:1.20.5-0-rootless

3
homelab/lidarr/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

10
homelab/lidarr/run Executable file
View File

@ -0,0 +1,10 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name $me \
--network homelab \
--volume /srv/sys/lidarr:/config \
--volume /srv/media/music:/srv/media/music \
--volume /srv/incoming:/srv/incoming \
lscr.io/linuxserver/lidarr

Binary file not shown.

View File

@ -1,5 +0,0 @@
[global]
update every = 10
[plugin:proc:/proc/net/dev]
disable by default interfaces matching = lo veth* docker*

3
homelab/nzbget/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

9
homelab/nzbget/run Executable file
View File

@ -0,0 +1,9 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name $me \
--network homelab \
--volume /srv/sys/nzbget:/config \
--volume /srv/incoming:/srv/incoming \
lscr.io/linuxserver/nzbget

3
homelab/oscar/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

20
homelab/oscar/run Executable file
View File

@ -0,0 +1,20 @@
#! /bin/sh
me=$(basename $(pwd))
image=git.woozle.org/neale/toolbox
podman pull $image
podman rm $me
podman run \
--name $me \
--hostname oscar \
--security-opt label=disable \
--privileged \
--network host \
--pid host \
--volume /srv/sys/oscar:/mnt/toolbox \
--volume /srv:/srv \
--volume /media:/media \
--volume /home:/home \
--volume /dev:/dev \
--volume /:/host \
$image

3
homelab/picoshare/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

10
homelab/picoshare/run Executable file
View File

@ -0,0 +1,10 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name $me \
--network homelab \
--env PS_SHARED_SECRET_FILE=/run/secrets/picoshare \
--volume /srv/sys/picoshare:/data \
--volume /srv/secrets/picoshare:/run/secrets/picoshare \
git.woozle.org/neale/picoshare

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

3
homelab/pigallery2/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

12
homelab/pigallery2/run Executable file
View File

@ -0,0 +1,12 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name $me \
--network homelab \
--volume /srv/sys/pigallery2/config:/app/data/config \
--volume /srv/sys/pigallery2/db:/app/data/db \
--volume /srv/sys/pigallery2/cache:/app/data/cache \
--volume /srv/storage/fam/Photos:/photos/fam:ro \
--volume /srv/storage/ginnie/Photos:/photos/ginnie:ro \
bpatrik/pigallery2:latest

3
homelab/plex/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

11
homelab/plex/run Executable file
View File

@ -0,0 +1,11 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name $me \
--network host \
--tz US/Mountain \
--env VERSION=public \
--mount type=bind,src=/srv/sys/plex,dst=/config \
--mount type=bind,src=/srv/media,dst=/srv/media,readonly=true \
lscr.io/linuxserver/plex:latest

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

3
homelab/portal/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

View File

@ -0,0 +1,76 @@
[
{
"title": "Storage",
"href": "https://drive.woozle.org/",
"icon": "https://drive.woozle.org/storage/public/icons/cloud-folder.png",
"target": "_blank"
},
{
"title": "Photos",
"href": "https://photos.woozle.org/",
"icon": "https://photos.woozle.org/icon.png",
"target": "_blank"
},
{
"title": "Git",
"href": "https://git.woozle.org/",
"icon": "https://git.woozle.org/assets/img/logo.svg",
"target": "_blank"
},
{
"title": "Genealogy",
"href": "https://ancestry.woozle.org/",
"icon": "https://ancestry.woozle.org/images/arbre_start.png",
"target": "_blank"
},
{
"title": "Movies",
"href": "https://deergrove.woozle.org/radarr/",
"icon": "/radarr/Content/Images/logo.svg"
},
{
"title": "Episodes",
"href": "https://deergrove.woozle.org/sonarr/",
"icon": "/sonarr/Content/Images/logo.svg"
},
{
"title": "Music",
"href": "https://deergrove.woozle.org/lidarr/",
"icon": "/lidarr/Content/Images/logo.svg"
},
{
"title": "Books",
"href": "https://deergrove.woozle.org/readarr/",
"icon": "/readarr/Content/Images/logo.svg"
},
{
"title": "Media Sucker",
"href": "https://deergrove.woozle.org/sucker/",
"icon": "/sucker/cd-dvd.svg"
},
{
"title": "Searcher",
"href": "https://deergrove.woozle.org/prowlarr/",
"icon": "/prowlarr/Content/Images/logo.png"
},
{
"title": "Usenet",
"href": "https://deergrove.woozle.org/nzbget/",
"icon": "/nzbget/img/favicon-256x256.png"
},
{
"title": "BitTorrent",
"href": "https://deergrove.woozle.org/transmission/web/",
"icon": "/transmission/web/images/webclip-icon.png"
},
{
"title": "3D Printer",
"href": "https://deergrove.woozle.org/octoprint/",
"icon": "/octoprint/static/img/logo.png"
},
{
"title": "Host Stats",
"href": "/stat.html",
"app": "stat"
}
]

9
homelab/portal/run Executable file
View File

@ -0,0 +1,9 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name $me \
--network homelab \
--volume ./portal.json:/web/portal.json:ro \
--volume ./deergrove.png:/web/portal.png:ro \
git.woozle.org/neale/portal

3
homelab/prowlarr/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

8
homelab/prowlarr/run Executable file
View File

@ -0,0 +1,8 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name $me \
--network homelab \
--volume /srv/sys/prowlarr:/config \
lscr.io/linuxserver/prowlarr

3
homelab/radarr/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

10
homelab/radarr/run Executable file
View File

@ -0,0 +1,10 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name $me \
--network homelab \
--volume /srv/sys/radarr:/config \
--volume /srv/media/movies:/srv/media/movies \
--volume /srv/incoming:/srv/incoming \
lscr.io/linuxserver/radarr

3
homelab/readarr/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

11
homelab/readarr/run Executable file
View File

@ -0,0 +1,11 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name $me \
--network homelab \
--volume /srv/sys/readarr:/config \
--volume /srv/media/books:/srv/media/books \
--volume /srv/media/audiobooks:/srv/media/audiobooks \
--volume /srv/incoming:/srv/incoming \
lscr.io/linuxserver/readarr:develop

3
homelab/samba/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

17
homelab/samba/run Executable file
View File

@ -0,0 +1,17 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name $me \
--network homelab \
--publish 139:139 \
--publish 445:445 \
--env NMBD=enable \
--env RECYCLE=disable \
--env USERID=911 \
--env GROUPID=911 \
--env SHARE1='drive;/srv;yes;no;no' \
--env SHARE2='media;/srv/media;yes;yes;yes' \
--env-file /srv/secrets/samba-users.env \
--volume /srv:/srv \
dperson/samba

3
homelab/simpleauth/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

9
homelab/simpleauth/run Executable file
View File

@ -0,0 +1,9 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name $me \
--network homelab \
--volume /srv/secrets/passwd:/run/secrets/passwd:ro \
--volume /srv/secrets/simpleauth.key:/run/secrets/simpleauth.key:ro \
git.woozle.org/neale/simpleauth

3
homelab/sonarr/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

10
homelab/sonarr/run Executable file
View File

@ -0,0 +1,10 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name $me \
--network homelab \
--volume /srv/sys/sonarr:/config \
--volume /srv/media/tv:/srv/media/tv \
--volume /srv/incoming:/srv/incoming \
lscr.io/linuxserver/sonarr

3
homelab/sucker/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

15
homelab/sucker/run Executable file
View File

@ -0,0 +1,15 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name=$me \
--device-cgroup-rule 'b 11:* rmw' \
--network homelab \
-v /dev:/hdev \
-v /srv/ext/incoming/sucker:/incoming/sucker \
--cpu-shares 256 \
--user 911:19 \
git.woozle.org/neale/media-sucker \
-incoming /incoming/sucker \
-drive /hdev/sr0 /hdev/sr1 /hdev/sr2

11
homelab/sync.sh Executable file
View File

@ -0,0 +1,11 @@
#! /bin/sh
service=/host/etc/service
if ! [ -d $service ]; then
echo "=== $service is not a directory" 1>&2
exit 1
fi
rsync -vaxp --delete \
--exclude=supervise/ \
$service/* .

14
homelab/sys-backup/run Executable file
View File

@ -0,0 +1,14 @@
#! /bin/sh
set -e
. /etc/lbu/lbu.conf
hostname=$(hostname)
mkdir -p /srv/backups/$hostname/
install -p -m 0600 /media/$LBU_MEDIA/$hostname.*.tar.gz /srv/backups/$hostname/
for path in /srv/backups/$hostname/$hostname.*.tar.gz; do
filename=$(basename $path)
[ -f /media/$LBU_MEDIA/$filename ] || rm $path
done
sleep 4h

3
homelab/transmission/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

12
homelab/transmission/run Executable file
View File

@ -0,0 +1,12 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name $me \
--network homelab \
--publish 51413:51413 \
--publish 51413:51413/udp \
--env PEERPORT=51413 \
--volume /srv/sys/transmission:/config \
--volume /srv/incoming:/srv/incoming \
lscr.io/linuxserver/transmission:latest

View File

@ -1,58 +0,0 @@
version: "3.8"
services:
syncthing:
image: syncthing/syncthing
environment:
PUID: 911
CGID: 911
volumes:
- type: bind
source: /srv/ext/sys/syncthing
target: /var/syncthing
- type: bind
source: /srv/ext
target: /srv/ext
ports:
- published: 22000
target: 22000
protocol: tcp
- published: 22000
target: 22000
protocol: udp
- published: 21027
target: 21027
protocol: udp
deploy:
labels:
traefik.enable: "true"
traefik.http.routers.syncthing.rule: "PathPrefix(`/syncthing`)"
traefik.http.routers.syncthing.middlewares: syncthing-striparoo
traefik.http.middlewares.syncthing-striparoo.stripprefix.prefixes: "/syncthing"
traefik.http.services.syncthing.loadbalancer.server.port: "8384"
## Drop this in to get a netdata container.
## It uses a lot of RAM and causes my machine to swap.
## Granted, it's a lot more lightweight than nextcloud.
## But I can live without netdata.
netdata:
image: netdata/netdata
hostname: $HOSTNAME
volumes:
- type: bind
source: /
target: /host
read_only: true
configs:
- source: netdata.conf
target: /etc/netdata/netdata.conf
deploy:
labels:
traefik.http.routers.netdata.rule: "PathPrefix(`/netdata`)"
traefik.http.middlewares.netdata-striparoo.stripprefix.prefixes: "/netdata"
traefik.http.routers.netdata.middlewares: netdata-striparoo
traefik.http.services.netdata.loadbalancer.server.port: "19999"
configs:
netdata.conf:
file: netdata.conf
name: netdata.conf-v4

View File

@ -1,10 +0,0 @@
[server]
domain = {{.Node.Hostname}}
root_url = %(protocol)s://%(domain)s:%(http_port)s/grafana/
serve_from_sub_path = true
[auth.anonymous]
enabled = false
org_name = Main Org.
org_role = Viewer

View File

@ -1,363 +0,0 @@
version: "3.8"
services:
coredns:
image: coredns/coredns
networks:
- hostnet
configs:
- source: Corefile
target: /Corefile
caddy:
image: caddy:2-alpine
ports:
- target: 443
published: 443
mode: host
- target: 80
published: 80
mode: host
volumes:
- type: bind
source: /srv
target: /srv
read_only: true
- type: bind
source: /srv/sys/caddy
target: /data/caddy
configs:
- source: Caddyfile
target: /etc/caddy/Caddyfile
- source: index.html
target: /www/index.html
- source: index.mjs
target: /www/index.mjs
- source: deergrove.png
target: /www/deergrove.png
- source: index.css
target: /www/index.css
- source: browser.html
target: /browser.html
extra_hosts:
- host.docker.internal:host-gateway
authelia:
image: authelia/authelia
environment:
AUTHELIA_JWT_SECRET_FILE: /run/secrets/jwt.secret
AUTHELIA_SESSION_SECRET_FILE: /run/secrets/session.secret
AUTHELIA_STORAGE_ENCRYPTION_FILE: /run/secrets/storage.secret
secrets:
- jwt.secret
- session.secret
- storage.secret
- users.yaml
configs:
- source: authelia.yaml
target: /config/configuration.yml
volumes:
- type: bind
source: /srv/sys/authelia
target: /srv/sys/authelia
jellyfin:
image: jellyfin/jellyfin
environment:
TZ: US/Mountain
volumes:
- type: bind
source: /srv/sys/jellyfin/config
target: /config
- type: bind
source: /srv/sys/jellyfin/cache
target: /cache
- type: bind
source: /srv/media
target: /srv/media
read_only: true
plex:
image: ghcr.io/linuxserver/plex:1.29.2
networks:
- hostnet
environment:
TZ: US/Mountain
VERSION: public
volumes:
- type: bind
source: /srv/sys/plex
target: /config
- type: bind
source: /srv
target: /srv
read_only: true
transmission:
image: lscr.io/linuxserver/transmission
volumes:
- type: bind
source: /srv/sys/transmission
target: /config
- type: bind
source: /srv/incoming
target: /srv/incoming
networks:
- hostnet
sonarr:
image: lscr.io/linuxserver/sonarr
extra_hosts:
- host.docker.internal:host-gateway
volumes:
- type: bind
source: /srv/sys/sonarr
target: /config
- type: bind
source: /srv/media/tv
target: /srv/media/tv
- type: bind
source: /srv/incoming
target: /srv/incoming
radarr:
image: lscr.io/linuxserver/radarr
extra_hosts:
- host.docker.internal:host-gateway
volumes:
- type: bind
source: /srv/sys/radarr
target: /config
- type: bind
source: /srv/media/movies
target: /srv/media/movies
- type: bind
source: /srv/incoming
target: /srv/incoming
lidarr:
image: lscr.io/linuxserver/lidarr
extra_hosts:
- host.docker.internal:host-gateway
volumes:
- type: bind
source: /srv/sys/lidarr
target: /config
- type: bind
source: /srv/media/music
target: /srv/media/music
- type: bind
source: /srv/incoming
target: /srv/incoming
readarr:
image: lscr.io/linuxserver/readarr:develop
extra_hosts:
- host.docker.internal:host-gateway
volumes:
- type: bind
source: /srv/sys/readarr
target: /config
- type: bind
source: /srv/media/books
target: /srv/media/books
- type: bind
source: /srv/media/audiobooks
target: /srv/media/audiobooks
- type: bind
source: /srv/incoming
target: /srv/incoming
prowlarr:
image: lscr.io/linuxserver/prowlarr:latest
extra_hosts:
- host.docker.internal:host-gateway
volumes:
- type: bind
source: /srv/sys/prowlarr
target: /config
nzbget:
image: lscr.io/linuxserver/nzbget
volumes:
- type: bind
source: /srv/sys/nzbget
target: /config
- type: bind
source: /srv/incoming
target: /srv/incoming
forgejo:
image: codeberg.org/forgejo/forgejo:1.18-rootless
secrets:
- source: forgejo.ini
target: /etc/gitea/app.ini
uid: "1000"
gid: "1000"
mode: 0400
volumes:
- type: bind
source: /srv/sys/forgejo
target: /data
- type: bind
source: /etc/timezone
target: /etc/timezone
read_only: true
- type: bind
source: /etc/localtime
target: /etc/localtime
read_only: true
atlas:
image: ctassisf/ripe-atlas-alpine:arm64v8
volumes:
- type: bind
source: /srv/sys/atlas/etc
target: /var/atlas-probe/etc
- type: bind
source: /srv/sys/atlas/status
target: /var/atlas-probe/status
geneweb:
image: ravermeister/geneweb
volumes:
- type: bind
source: /srv/sys/geneweb/etc
target: /usr/local/share/geneweb/etc
- type: bind
source: /srv/sys/geneweb/share/data
target: /usr/local/share/geneweb/share/data
- type: bind
source: /srv/sys/geneweb/log
target: /usr/local/share/geneweb/log
samba:
image: dperson/samba
volumes:
- type: bind
source: /srv
target: /srv
bind:
propagation: rslave
environment:
NMBD: enable
RECYCLE: disable
USERID: 911
GROUPID: 911
# name;path;browse;readonly;guest
SHARE1: drive;/srv;yes;no;no
SHARE2: retropie;/srv/media/games/retropie;yes;yes;yes
env_file:
- secrets/samba-users.env
ports:
- published: 139
target: 139
- published: 445
target: 445
webdav:
image: micromata/dave
volumes:
- type: bind
source: /srv
target: /data
configs:
- source: dave.yaml
target: /config/config.yaml
user: "911:911"
ddns:
image: qmcgaw/ddns-updater
dns:
- 1.1.1.1
volumes:
- type: bind
source: /srv/sys/ddns-updater
target: /updater/data
tunnel:
deploy:
replicas: 0
image: lscr.io/linuxserver/openssh-server
user: abc
entrypoint:
- /usr/bin/ssh
- -N
- -R 172.17.0.1:5880:caddy:80 # 172.17.0.1 = docker host IP
- -R :5822:host.docker.internal:22
- -o ServerAliveInterval=30
- core@melville.woozle.org
extra_hosts:
- host.docker.internal:host-gateway
secrets:
- source: tunnel
target: /config/.ssh/id_rsa
uid: "911"
gid: "911"
mode: 0600
- source: known_hosts
target: /config/.ssh/known_hosts
uid: "911"
gid: "911"
mode: 0600
configs:
dave.yaml:
file: dave.yaml
name: dave.yaml-v3
Corefile:
file: Corefile
name: Corefile-v3
Caddyfile:
file: Caddyfile
name: Caddyfile-v89
index.html:
file: www/index.html
name: index.html-v36
index.mjs:
file: www/index.mjs
name: index.mjs-v1
index.css:
file: www/index.css
name: index.css-v1
browser.html:
file: www/browser.html
name: browser.html-v3
deergrove.png:
file: www/deergrove.png
name: deergrove.png-v1
authelia.yaml:
file: authelia.yaml
name: authelia.yaml-v1
secrets:
passwd:
file: secrets/passwd
name: passwd-v2
simpleauth.key:
file: secrets/simpleauth.key
name: simpleauth.key-v1
tunnel:
file: secrets/tunnel
name: tunnel-v1
known_hosts:
file: secrets/known_hosts
name: known_hosts-v1
forgejo.ini:
file: secrets/forgejo.ini
name: forgejo.ini-v1
jwt.secret:
file: secrets/jwt.secret
name: jwt.secret-v1
storage.secret:
file: secrets/storage.secret
name: storage.secret-v1
session.secret:
file: secrets/session.secret
name: session.secret-v1
users.yaml:
file: secrets/users.yaml
name: users.yaml-v1
networks:
hostnet:
external: true
name: host

View File

@ -1,252 +0,0 @@
version: "3.8"
services:
traefik:
image: traefik
environment:
TRAEFIK_API: "true"
TRAEFIK_API_INSECURE: "true"
TRAEFIK_ENTRYPOINTS_WEB_ADDRESS: :80
TRAEFIK_ENTRYPOINTS_WEB_HTTP_REDIRECTIONS_ENTRYPOINT_TO: websecure
TRAEFIK_ENTRYPOINTS_WEB_HTTP_REDIRECTIONS_ENTRYPOINT_SCHEME: https
TRAEFIK_ENTRYPOINTS_WEBSECURE_ADDRESS: :443
TRAEFIK_ENTRYPOINTS_WEBSECURE_HTTP_TLS_CERTRESOLVER: letsencrypt
TRAEFIK_CERTIFICATESRESOLVERS_LETSENCYRPT_ACME_EMAIL: neale@woozle.org
TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_STORAGE: /acme.json
XXX_TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_HTTPCHALLENGE_ENTRYPOINT: web
TRAEFIK_CERTIFICATESRESOLVERS_LETSENCRYPT_ACME_TLSCHALLENGE: "true"
TRAEFIK_PROVIDERS_DOCKER_SWARMMODE: "true"
TRAEFIK_PROVIDERS_DOCKER_EXPOSEDBYDEFAULT: "false"
ports:
- target: 443
published: 443
mode: host
- target: 80
published: 80
mode: host
volumes:
- type: bind
source: /var/run/docker.sock
target: /var/run/docker.sock
read_only: true
- type: bind
source: /srv/ext/sys/traefik/acme.json
target: /acme.json
deploy:
labels:
# XXX: This HSTS stuff doesn't seem to be working
traefik.enable: "true"
traefik.frontend.headers.STSSeconds: "31536000"
traefik.frontend.headers.STSPreload: "true"
traefik.http.routers.dashboard.rule: "Host(`$FQDN`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
traefik.http.routers.dashboard.tls.certresolver: letsencrypt
traefik.http.routers.dashboard.middlewares: forward-auth
traefik.http.routers.dashboard.service: api@internal
traefik.http.middlewares.forward-auth.forwardauth.address: http://simpleauth:8080/
traefik.http.services.traefik.loadbalancer.server.port: "1"
simpleauth:
image: ghcr.io/nealey/simpleauth
secrets:
- password
deploy:
labels:
traefik.enable: "true"
traefik.http.routers.simpleauth.rule: "Host(`$FQDN`) && Path(`/`)"
traefik.http.services.simpleauth.loadbalancer.server.port: "8080"
plex:
image: ghcr.io/linuxserver/plex
networks:
- hostnet
environment:
TZ: US/Mountain
VERSION: public
volumes:
- type: bind
source: /srv/ext/sys/plex
target: /config
- type: bind
source: /srv
target: /srv
read_only: true
bind:
propagation: rslave
transmission:
image: ghcr.io/linuxserver/transmission
networks:
- hostnet
volumes:
- type: bind
source: /srv/ext/sys/transmission
target: /config
- type: bind
source: /srv/ext/incoming
target: /srv/ext/incoming
deploy:
labels:
# This isn't going to work, because transmission binds to the host network.
traefik.http.routers.transmission.rule: "PathPrefix(`/transmission`)"
traefik.http.services.transmission.loadbalancer.server.port: "9091"
ocis:
image: owncloud/ocis:2.0.0-beta.5
environment:
OCIS_URL: https://drive.woozle.org/
PROXY_TLS: "false"
# ports:
# - published: 9200
# target: 9200
volumes:
- type: bind
source: /srv/ext/sys/ocis/config
target: /etc/ocis
- type: bind
source: /srv/ext/sys/ocis/data
target: /var/lib/ocis
deploy:
labels:
traefik.enable: "true"
traefik.http.routers.ocis.rule: "Host(`drive.woozle.org`)"
traefik.http.routers.ocis.tls: "true"
traefik.http.routers.ocis.tls.certresolver: letsencrypt
traefik.http.services.ocis.loadbalancer.server.port: "9200"
gitea:
image: gitea/gitea:1
environment:
USER_UID: 1000
USER_GID: 1000
volumes:
- type: bind
source: /srv/ext/sys/gitea
target: /data
- type: bind
source: /etc/timezone
target: /etc/timezone
read_only: true
- type: bind
source: /etc/localtime
target: /etc/localtime
read_only: true
deploy:
labels:
traefik.enable: "true"
traefik.http.routers.gitea.rule: "Host(`git.woozle.org`)"
traefik.http.routers.gitea.middlewares: gitea-striparoo
traefik.http.middlewares.gitea-striparoo.stripprefix.prefixes: "/gitea"
traefik.http.services.gitea.loadbalancer.server.port: "3000"
atlas:
image: ctassisf/ripe-atlas-alpine:arm64v8
volumes:
- type: bind
source: /srv/ext/sys/atlas/etc
target: /var/atlas-probe/etc
- type: bind
source: /srv/ext/sys/atlas/status
target: /var/atlas-probe/status
networks:
- hostnet
## Drop this in to get a netdata container.
## It uses a lot of RAM and causes my machine to swap.
## Granted, it's a lot more lightweight than nextcloud.
## But I can live without netdata.
# netdata:
# image: netdata/netdata
# hostname: $HOSTNAME
# volumes:
# - type: bind
# source: /
# target: /host
# read_only: true
# configs:
# - source: netdata.conf
# target: /etc/netdata/netdata.conf
# deploy:
# labels:
# traefik.http.routers.netdata.rule: "PathPrefix(`/netdata`)"
# traefik.http.middlewares.netdata-striparoo.stripprefix.prefixes: "/netdata"
# traefik.http.routers.netdata.middlewares: netdata-striparoo
# traefik.http.services.netdata.loadbalancer.server.port: "19999"
geneweb:
image: ravermeister/geneweb
volumes:
- type: bind
source: /srv/ext/sys/geneweb/etc
target: /usr/local/share/geneweb/etc
- type: bind
source: /srv/ext/sys/geneweb/share/data
target: /usr/local/share/geneweb/share/data
- type: bind
source: /srv/ext/sys/geneweb/log
target: /usr/local/share/geneweb/log
deploy:
labels:
traefik.enable: "true"
traefik.http.routers.gwsetup.rule: "PathPrefix(`/gwsetup`)"
traefik.http.middlewares.gwsetup-striparoo.stripprefix.prefixes: "/gwsetup"
traefik.http.routers.gwsetup.middlewares: gwsetup-striparoo,forward-auth
traefik.http.routers.gwsetup.service: gwsetup
traefik.http.services.gwsetup.loadbalancer.server.port: "2316"
traefik.http.routers.geneweb.rule: "Host(`ancestry.woozle.org`)"
traefik.http.routers.geneweb.service: geneweb
traefik.http.services.geneweb.loadbalancer.server.port: "2317"
samba:
image: dperson/samba
volumes:
- type: bind
source: /srv/ext
target: /srv/ext
bind:
propagation: rslave
environment:
NMBD: enable
RECYCLE: disable
USERID: 911
GROUPID: 911
# name;path;browse;readonly;guest
SHARE1: storage;/srv/ext/storage;yes;no;no
SHARE2: media;/srv/ext/media;yes;no;no
SHARE3: software;/srv/ext/software;yes;no;no
SHARE4: backups;/srv/ext/backups;yes;no;no
SHARE4: incoming;/srv/ext/incoming;yes;no;no
env_file:
- samba-users.env
ports:
- published: 139
target: 139
- published: 445
target: 445
public:
image: caddy
volumes:
- type: bind
source: /srv/ext/storage/public
target: /usr/share/caddy/public
read_only: true
deploy:
labels:
traefik.enable: "true"
traefik.http.routers.public.rule: "PathPrefix(`/public`)"
traefik.http.services.public.loadbalancer.server.port: "80"
configs:
netdata.conf:
file: netdata.conf
name: netdata.conf-v4
secrets:
password:
file: password
name: password-v1
networks:
hostnet:
external: true
name: host

View File

@ -1,3 +0,0 @@
#! /bin/sh
tar czf /mnt/ardenne/backups/sweetums.tar.gz /host/etc/systemd

View File

@ -1,9 +0,0 @@
#! /bin/sh
# We do this on the host's cron now
exit 0
for mnt in /mnt/ardenne; do
chroot /host /usr/bin/btrfs scrub start -B $mnt
done

View File

@ -1,38 +0,0 @@
#! /bin/sh
domain=woozle.org
host=sweetums
key=6SRjTBJG7v2bHyG9gqX913w1
log() {
echo "=== $*"
}
json() {
ip4=$1
ip6=$2
printf '{"items": ['
if [ -n "$ip6" ]; then
printf '{"rrset_type":"AAAA","rrset_values":["%s"],"rrset_ttl":300},' "$ip6"
fi
printf '{"rrset_type":"A","rrset_values":["%s"],"rrset_ttl":300}' "$ip4"
printf ']}'
}
push() {
host=$1
domain=$2
authh="Authorization: Apikey $key"
log "$host.$domain"
curl -s -H "$authh" -T - https://api.gandi.net/v5/livedns/domains/$domain/records/$host
echo
}
me4=$(curl -s -4 https://api64.ipify.org/)
me6=$(curl -s -6 https://api64.ipify.org/)
log "IPv4: $me4"
log "IPv6: $me6"
json $me4 $me6 | push sweetums woozle.org
json $me4 | push drive woozle.org

View File

@ -1,11 +0,0 @@
#! /bin/sh -e
for user in neale amy ginnie; do
dir=/mnt/ext/backups/$user
rclone \
--config=$dir/rclone.conf \
--bwlimit=2M \
--transfers=1 \
sync gdrive: $dir/gdrive
chown -R $(stat -c %u:%g $dir) $dir
done

View File

@ -1,3 +0,0 @@
#! /bin/sh
date > /mnt/ext/srv/periodic/last-run

View File

@ -1,4 +0,0 @@
#! /bin/sh
[ $(hostname) = sweetums ] || exit 0
wget -O - https://drive.woozle.org/cron.php >/dev/null

View File

@ -1,7 +0,0 @@
global:
scrape_configs:
- job_name: node_exporter
static_configs:
- targets:
- node-exporter:9100

3
homelab/webfs/finish Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
me=$(basename $(pwd))
podman stop $me

11
homelab/webfs/run Executable file
View File

@ -0,0 +1,11 @@
#! /bin/sh
me=$(basename $(pwd))
podman rm $me
podman run \
--name $me \
--network homelab \
--user 911:911 \
--volume /srv/storage:/srv/storage \
--volume /srv/incoming:/srv/incoming \
--volume /srv/media:/srv/media \
sigoden/dufs -A /srv

View File

@ -1,78 +0,0 @@
body {
display: flex;
flex-flow: column;
height: 100vh;
margin: 0;
font-family: Helvetica, sans-serif;
background-color: #222;
color: white;
}
.hidden {
display: none;
}
nav {
overflow-x: auto;
font-weight: bold;
font-size: 12pt;
display: flex;
}
nav img {
max-height: 2em;
}
nav a {
padding: 0.5em 1.5em;
color: #aaa;
text-decoration: none;
white-space: nowrap;
}
nav a[data-no-menu] {
display: none;
}
nav a:hover {
background: #555;
}
nav a.active {
background: #8b8;
color: black;
}
#app {
flex-grow: 1;
}
iframe {
display: block;
border: none;
width: 100%;
height: 100%;
}
.icons {
margin: auto 2em;
}
.icons {
display: flex;
flex-wrap: wrap;
justify-content: center;
max-width: 40em;
margin: auto;
gap: 20px;
}
.icons a {
background-color: #444;
color: #fff;
width: 120px;
height: 120px;
border-radius: 10px;
display: flex;
justify-content: center;
align-items: center;
}
.icons a img {
max-width: 66%;
max-height: 66%;
width: 100%;
height: 100%;
image-rendering: pixelated;
}

View File

@ -1,18 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Deer Grove</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="deergrove.png">
<link rel="stylesheet" href="index.css">
<script src="index.mjs" type="module"></script>
</head>
<body>
<nav>
</nav>
<section id="app">
<iframe></iframe>
</section>
</body>
</html>

View File

@ -1,109 +0,0 @@
let frames = {}
function activate(event, element) {
event.preventDefault()
let parent = element.parentElement
for (let e of parent.getElementsByClassName("active")) {
e.classList.remove("active")
}
element.classList.add("active")
let href = element.href
let app = document.querySelector("#app")
let frame = frames[href]
if (frame) {
frame.dispatchEvent(new Event("load"))
} else {
frame = app.appendChild(document.createElement("iframe"))
frame.addEventListener("load", e => frameLoaded(frame))
frame.src = href
frames[href] = frame
}
for (let fhref in frames) {
let f = frames[fhref]
if (fhref == href) {
f.classList.remove("hidden")
} else {
f.classList.add("hidden")
}
}
}
function frameLoaded(frame) {
let doc = frame.contentDocument
if (doc.title.length > 0) {
document.title = doc.title
}
let icon = document.querySelector("link[rel~='icon']")
let dicon = doc.querySelector("link[rel~='icon']")
if (dicon) {
icon.href = dicon.href
} else {
icon.href = defaultIcon
}
}
let defaultIcon = null
async function init() {
let doc = document.querySelector("iframe").contentDocument
defaultIcon = document.querySelector("link[rel~='icon']").href
for (let l of document.head.querySelectorAll("style")) {
doc.head.appendChild(l.cloneNode(true))
}
for (let l of document.head.querySelectorAll("link[rel='stylesheet']")) {
doc.head.appendChild(l.cloneNode())
}
for (let f of document.querySelectorAll("#app iframe")) {
frames[f.src] = f
}
let icons = doc.body.appendChild(doc.createElement("section"))
icons.classList.add("icons")
let portalURL = new URL("portal.json", window.location)
let resp = await fetch(portalURL)
let obj = await resp.json()
let nav = document.querySelector("nav")
for (let app of obj) {
let hlink = null
if (app.target != "_blank") {
hlink = nav.appendChild(document.createElement("a"))
hlink.href = app.href
hlink.textContent = app.title
if (app.target) {
hlink.target = app.target
} else {
hlink.addEventListener("click", event => activate(event, hlink))
}
}
let dlink = icons.appendChild(doc.createElement("a"))
dlink.href = app.href
if (app.target) {
dlink.target = app.target
} else {
dlink.addEventListener("click", event => activate(event, hlink))
}
if (app.icon) {
let icon = dlink.appendChild(doc.createElement("img"))
icon.src = app.icon
icon.alt = app.title
icon.title = app.title
icon.style.objectFit = "cover"
} else {
let text = dlink.appendChild(doc.createElement("div"))
text.textContent = app.title
}
}
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init)
} else {
init()
}