commit cbf4c967161ed814b903a636929817a6bbe8dd1e
parent 81d004b7644d9871420abd06cef84b8bc667e535
Author: Robin Bron <robin.bron@yourhosting.nl>
Date: Mon, 2 Mar 2026 22:51:40 +0100
Docker setup
Diffstat:
4 files changed, 215 insertions(+), 0 deletions(-)
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
@@ -0,0 +1,68 @@
+---
+name: Docker Build & Push
+
+on:
+ push:
+ branches:
+ - main
+ - '*.feature'
+ - '*.fix'
+ tags:
+ - 'v*'
+ pull_request:
+ branches:
+ - main
+ workflow_dispatch:
+
+env:
+ REGISTRY: docker.io
+ IMAGE_NAME: finwo/udphole
+
+jobs:
+ docker:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ packages: write
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v3
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Log in to Docker Hub
+ if: github.event_name != 'pull_request'
+ uses: docker/login-action@v3
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+
+ - name: Extract metadata
+ id: meta
+ uses: docker/metadata-action@v5
+ with:
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
+ tags: |
+ type=ref,event=branch
+ type=ref,event=pr
+ type=semver,pattern={{version}}
+ type=semver,pattern={{major}}
+ type=semver,pattern={{major}}.{{minor}}
+ type=semver,pattern={{major}}.{{minor}}.{{patch}}
+ type=raw,value=latest,enable={{is_default_branch}}
+
+ - name: Build and push
+ uses: docker/build-push-action@v5
+ with:
+ context: .
+ platforms: linux/amd64,linux/arm64/v8,linux/riscv64
+ push: ${{ github.event_name != 'pull_request' }}
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
+ cache-from: type=gha
+ cache-to: type=gha,mode=max
diff --git a/Dockerfile b/Dockerfile
@@ -0,0 +1,37 @@
+FROM debian:bookworm-slim AS builder
+
+RUN apt-get update && apt-get install -y \
+ gcc \
+ make \
+ wget \
+ crossbuild-essential-amd64 \
+ crossbuild-essential-arm64 \
+ crossbuild-essential-riscv64
+
+RUN wget -O /usr/local/bin/dep https://raw.githubusercontent.com/finwo/dep/master/dep && \
+ chmod +x /usr/local/bin/dep
+
+WORKDIR /src
+COPY . .
+
+RUN dep ensure && \
+ make -j \
+ CC=aarch64-linux-gnu-gcc CFLAGS="-static-libgcc" TARGETARCH=arm64 && \
+ mv udphole udphole-arm64 && \
+ make clean && \
+ make -j \
+ CC=riscv64-linux-gnu-gcc CFLAGS="-static-libgcc" TARGETARCH=riscv64 && \
+ mv udphole udphole-riscv64 && \
+ make clean && \
+ make -j \
+ CC=gcc CFLAGS="-static-libgcc" TARGETARCH=amd64 && \
+ mv udphole udphole-amd64
+
+FROM --platform=${TARGETPLATFORM} busybox:latest
+
+COPY --from=builder /src/udphole-${TARGETARCH} /usr/bin/udphole
+COPY entrypoint.sh /etc/rc.local
+
+RUN chmod +x /etc/rc.local
+
+ENTRYPOINT ["/bin/ash", "/etc/rc.local"]
diff --git a/docker-compose.yml b/docker-compose.yml
@@ -0,0 +1,24 @@
+services:
+ udphole:
+ build:
+ context: .
+ platforms:
+ - linux/amd64
+ - linux/arm64/v8
+ - linux/riscv64
+ ports:
+ - "${API_PORT:-6379}:${API_PORT:-6379}"
+ - "7000-7999:7000-7999/udp"
+ volumes:
+ - ./udphole.conf:/etc/udphole.conf:ro
+ environment:
+ - API_PORT=6379
+ - UDP_PORTS=7000-7999
+ - LOG_LEVEL=info
+ - API_ADMIN_USER=admin
+ - API_ADMIN_PASS=supers3cret
+ healthcheck:
+ test: ["CMD", "nc", "-z", "localhost", "6379"]
+ interval: 10s
+ timeout: 5s
+ retries: 3
diff --git a/entrypoint.sh b/entrypoint.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+set -e
+
+CONFIG_PATH="/etc/udphole.conf"
+UDPHBIN="/usr/bin/udphole"
+
+if [ -f "$CONFIG_PATH" ]; then
+ echo "Using mounted config: $CONFIG_PATH"
+else
+ echo "Generating config from environment variables..."
+
+ {
+ echo "[udphole]"
+ echo "ports = ${UDP_PORTS:-7000-7999}"
+ echo "listen = :${API_PORT:-6379}"
+
+ if [ -n "$CLUSTER" ]; then
+ for name in $(echo "$CLUSTER" | tr ',' ' '); do
+ echo "cluster = $name"
+ done
+ fi
+
+ echo ""
+ echo "[user:${API_ADMIN_USER:-admin}]"
+ echo "permit = *"
+ echo "secret = ${API_ADMIN_PASS:-supers3cret}"
+
+ if [ -n "$CLUSTER" ]; then
+ for name in $(echo "$CLUSTER" | tr ',' ' '); do
+ env_var="CLUSTER_$name"
+ eval "value=\$$env_var"
+ if [ -n "$value" ]; then
+ proto="${value%%://*}"
+ rest="${value#*://}"
+
+ # Check if URL contains user:pass@ (has credentials)
+ case "$rest" in
+ *@*)
+ user="${rest%%:*}"
+ rest="${rest#*:}"
+ pass="${rest%%@*}"
+ rest="${rest#*@}"
+ has_creds=1
+ ;;
+ *)
+ user=""
+ pass=""
+ has_creds=0
+ ;;
+ esac
+
+ host="${rest%%:*}"
+ port="${rest#*:}"
+
+ echo ""
+ echo "[cluster:$name]"
+ echo "address = $value"
+ if [ "$has_creds" -eq 1 ]; then
+ echo "username = $user"
+ echo "password = $pass"
+ fi
+ fi
+ done
+ fi
+ } > "$CONFIG_PATH"
+
+ echo "Generated config:"
+ cat "$CONFIG_PATH"
+fi
+
+CMD="$UDPHBIN"
+
+if [ -n "$LOG_LEVEL" ]; then
+ CMD="$CMD --verbosity $LOG_LEVEL"
+fi
+
+if [ -n "$CLUSTER" ]; then
+ CMD="$CMD cluster"
+else
+ CMD="$CMD daemon"
+fi
+
+CMD="$CMD --no-daemonize"
+
+echo "Running: $CMD"
+exec $CMD