diff --git a/.github/workflows/build-and-push.yml b/.github/workflows/build-and-push.yml new file mode 100644 index 0000000..1208102 --- /dev/null +++ b/.github/workflows/build-and-push.yml @@ -0,0 +1,60 @@ +name: Build and Push Docker Image + +on: + workflow_dispatch: + push: + branches: + - main + - master + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + outputs: + container_sha: ${{ github.sha }} + registry_url: ${{ steps.registry.outputs.url }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set registry URL + id: registry + run: | + if [ "${{ github.server_url }}" = "https://github.com" ]; then + echo "url=ghcr.io" >> $GITHUB_OUTPUT + else + # Extract domain from server_url (e.g. gitea.service.dc1.fbleagh.duckdns.org) + echo "url=${{ github.server_url }}" | sed 's|https://||' >> $GITHUB_OUTPUT + fi + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Log in to Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ steps.registry.outputs.url }} + username: ${{ github.actor }} + password: ${{ secrets.PACKAGE_TOKEN || secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push multi-arch Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: true + platforms: linux/amd64,linux/arm64 + tags: | + ${{ steps.registry.outputs.url }}/${{ github.repository }}:latest + ${{ steps.registry.outputs.url }}/${{ github.repository }}:${{ github.sha }} + build-args: | + COMMIT_SHA=${{ github.sha }} + cache-from: type=registry,ref=${{ steps.registry.outputs.url }}/${{ github.repository }}:buildcache + cache-to: type=registry,ref=${{ steps.registry.outputs.url }}/${{ github.repository }}:buildcache,mode=max + labels: org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6707dfc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +# Stage 1: Get LiteFS binary +FROM flyio/litefs:0.5 AS litefs + +# Stage 2: Final image +FROM ghcr.io/navidrome/navidrome:latest + +# Install FUSE and CA certificates (needed for LiteFS) +USER root +RUN apk add --no-cache fuse3 ca-certificates + +# Copy LiteFS binary +COPY --from=litefs /usr/local/bin/litefs /usr/local/bin/litefs + +# Copy LiteFS configuration +COPY litefs.yml /etc/litefs.yml + +# We'll use environment variables for most LiteFS settings, +# but the baked-in config provides the structure. +# LiteFS will mount the FUSE fs and then execute Navidrome. +ENTRYPOINT ["litefs", "mount", "--", "/app/navidrome"] diff --git a/litefs.yml b/litefs.yml new file mode 100644 index 0000000..8042d2b --- /dev/null +++ b/litefs.yml @@ -0,0 +1,27 @@ +# LiteFS configuration for Navidrome +fuse: + dir: "/data" + +data: + dir: "/var/lib/litefs" + +# Use Consul for leader election +lease: + type: "consul" + consul: + url: "${CONSUL_URL}" + key: "litefs/navidrome" + +# The HTTP Proxy routes traffic to handle write-forwarding +proxy: + addr: ":${PORT}" + target: "localhost:4533" + db: "navidrome.db" + passthrough: + - "*.js" + - "*.css" + - "*.png" + - "*.jpg" + - "*.jpeg" + - "*.gif" + - "*.svg" diff --git a/navidrome-litefs-v2.nomad b/navidrome-litefs-v2.nomad new file mode 100644 index 0000000..dee7217 --- /dev/null +++ b/navidrome-litefs-v2.nomad @@ -0,0 +1,89 @@ +job "navidrome-litefs" { + datacenters = ["dc1"] + type = "service" + + constraint { + attribute = "${attr.kernel.name}" + value = "linux" + } + + group "navidrome" { + count = 2 + + constraint { + distinct_hosts = true + } + + network { + mode = "host" + port "http" {} + } + + task "navidrome" { + driver = "docker" + + config { + # Update this to match your actual registry and image name + image = "gitea.service.dc1.fbleagh.duckdns.org/sstent/navidrome-litefs:latest" + privileged = true # Needed for FUSE + network_mode = "host" + + volumes = [ + "/mnt/configs/navidrome_litefs:/var/lib/litefs", + "/mnt/Public/configs/navidrome:/shared_data", + "/mnt/Public/Downloads/Clean_Music:/music/CleanMusic:ro", + "/mnt/Public/Downloads/news/slskd/downloads:/music/slskd:ro", + "/mnt/Public/Downloads/incoming_music:/music/incomingmusic:ro" + ] + } + + env { + # LiteFS Config + CONSUL_URL = "http://${attr.unique.network.ip-address}:8500" + PORT = "${NOMAD_PORT_http}" + + # Navidrome Config + ND_DATAFOLDER = "/local/data" + ND_CACHEFOLDER = "/shared_data/cache" + ND_CONFIGFILE = "/local/data/navidrome.toml" + + # Database is on the LiteFS FUSE mount + ND_DBPATH = "/data/navidrome.db?_busy_timeout=30000&_journal_mode=WAL&_foreign_keys=on&synchronous=NORMAL" + + ND_SCANSCHEDULE = "0" + ND_SCANNER_FSWATCHER_ENABLED = "false" + ND_LOGLEVEL = "info" + ND_REVERSEPROXYWHITELIST = "0.0.0.0/0" + ND_REVERSEPROXYUSERHEADER = "X-Forwarded-User" + } + + service { + name = "navidrome" + tags = [ + "navidrome", + "web", + "traefik.enable=true", + "urlprefix-/navidrome", + "tools", + "traefik.http.routers.navidromelan.rule=Host(`navidrome.service.dc1.consul`)", + "traefik.http.routers.navidromewan.rule=Host(`m.fbleagh.duckdns.org`)", + "traefik.http.routers.navidromewan.middlewares=dex@consulcatalog", + "traefik.http.routers.navidromewan.tls=true", + ] + port = "http" + + check { + type = "http" + path = "/app" + interval = "10s" + timeout = "2s" + } + } + + resources { + cpu = 500 + memory = 512 + } + } + } +}