diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index f942c85..861b293 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -27,12 +27,12 @@ "state": { "type": "markdown", "state": { - "file": "README.md", + "file": "valheim.md", "mode": "source", "source": false }, "icon": "lucide-file", - "title": "README" + "title": "valheim" } } ], @@ -109,7 +109,7 @@ "state": { "type": "backlink", "state": { - "file": "README.md", + "file": "valheim.md", "collapseAll": false, "extraContext": false, "sortOrder": "alphabetical", @@ -119,7 +119,7 @@ "unlinkedCollapsed": true }, "icon": "links-coming-in", - "title": "Backlinks for README" + "title": "Backlinks for valheim" } }, { @@ -128,12 +128,12 @@ "state": { "type": "outgoing-link", "state": { - "file": "README.md", + "file": "valheim.md", "linksCollapsed": false, "unlinkedCollapsed": true }, "icon": "links-going-out", - "title": "Outgoing links from README" + "title": "Outgoing links from valheim" } }, { @@ -171,13 +171,13 @@ "state": { "type": "outline", "state": { - "file": "README.md", + "file": "valheim.md", "followCursor": false, "showSearch": false, "searchQuery": "" }, "icon": "lucide-list", - "title": "Outline of README" + "title": "Outline of valheim" } } ] @@ -201,8 +201,9 @@ "active": "1dd11189df0fe00f", "lastOpenFiles": [ "infra.md", - "README.md", + "valheim.md", "rikidown.md", + "README.md", "thoughts.md" ] } \ No newline at end of file diff --git a/README.md b/README.md index 696d131..dacf642 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ # Hello world! -It appears this does not support normal markdown, so this may be dropped awfully quick. Much sadness. - -It seems to be simple and fast though, so I'm conflicted. +This is powered by rikidown (see below); some previous text alluded a different software I was considering that ultimately got dropped because it was not using propert Markdown format. This seems to be working much better. [infra.md](infra.md) diff --git a/infra.md b/infra.md index 6df9b1a..0773398 100644 --- a/infra.md +++ b/infra.md @@ -1,9 +1,23 @@ -All of this is running on a Raspberry Pi 5 sitting in a closet. The Pi runs k3s, a lightweight Kubernetes implementation, and Tailscale to allow me to access resources when I'm away from home. +I acquired a used server for a very reasonable cost; it has 80x3.0ghz ARM cores, 128 GiB of RAM, and uses 70w of power. Power where I live is super expensive, so I'm very excited to have this beast available for minimal power cost. -## FluxCD +If you are interested in hosting anything, please reach out :). If I you don't know how to reach out, you probably aren't invited to reach out. -The cluster is managed by FluxCD. I describe my apps in kustomizations and plain YAML. +I also have 5pi5, a Raspberry Pi 5 (16 GiB) that I use to host smaller applications. -## SSL +## Configuration -SSL certs are issues through letsencrypt. I've pointed my name cheap domain name to Google Clouds DNS servers, then setup cert-manager in the Kubernetes cluster to acquire SSL certificates using the DNS challenge method. This allows me to have valid SSL certs with no client side configuration, and without actually exposing my service to the Internet (which would be required for the standard HTML challenge). +The very strong ARM machine (aka, machop) runs k3s. This allows me to store my configurations in FluxCD, kept in a Git repo. Very helpful in terms of my ability to work on one project a time, when tipsy. + +As a bonus, those silly AI tools are *very* helpful when you are tipsy. You can just ask it to: + +> add a new app to prod which uses the Docker image docker.tipsy.codes/rikidown:20251217. +> the app should include and ingress for wiki.tipsy.codes, and it should add the arguments '--git-repo https://git.tipsy.codes/charles/wiki.tipsy.codes.git'. +> the pod will expose port 8080, which should be wrapped in a service and used in the ingress + +and it will do the thing. It did pretty good, overall. + +The Git repo for my FluxCD configuration is not public because I'm not confident that I've correctly removed all private keys from it (notably, the keys to access the kubernetes dashboard). In principle it should still be fine because access to the k3s control plane is restricted to my local network, but all the same... I don't trust you. + +# Projects + +[Valheim](valheim.md) \ No newline at end of file diff --git a/valheim.md b/valheim.md new file mode 100644 index 0000000..ca3067f --- /dev/null +++ b/valheim.md @@ -0,0 +1,312 @@ +It turns out that you can run x86 applications on ARM machines, which is super cool. But it requires a bit of nonsense. Here is how I got the Valheim server to work. +## Dockerfile +I created a docker file like: +``` +# Use Ubuntu 22.04 as base +FROM weilbyte/box:debian-11 + +RUN apt update && apt upgrade -y +RUN apt install -y curl libpulse-dev libatomic1 libc6 + +# Create user steam +RUN useradd -m steam + +# Change user to steam +USER steam + +# Go to /home/steam/Steam +WORKDIR /home/steam/Steam + +# Download and extract SteamCMD +RUN curl -sqL "https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz" | tar zxvf - + +RUN /usr/local/bin/box64 /home/steam/Steam/steamcmd.sh +force_install_dir /home/steam/Valheim +login anonymous +app_update 896660 validate +exit + +WORKDIR /home/steam/Valheim + +COPY start_server.sh /home/steam/Valheim + +ENTRYPOINT ["/usr/local/bin/box64", "/home/steam/Valheim/start_server.sh"] +``` +I built and pushed this image with: +``` +docker build -t docker.tipsy.codes/valheim:20251214 . +``` +## k3s config +To deploy this, I made added some files to my Flux repo. Here is a tree: + +``` +❯ tree apps/prod/valheim +apps/prod/valheim +├── deployment.yaml +├── ingress.yaml +├── kustomization.yaml +├── namespace.yaml +├── pvc.yaml +├── pv.yaml +└── service.yaml + +1 directory, 7 files +``` +The most interesting files are: +``` +❯ find apps/prod/valheim/* -exec echo '# {}' \; -exec cat {} \; +# apps/prod/valheim/deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: valheim + namespace: valheim +spec: + replicas: 1 + selector: +   matchLabels: +     app: valheim + template: +   metadata: +     labels: +       app: valheim +   spec: +     containers: +       - name: valheim +         image: docker.tipsy.codes/valheim:20251214 +         ports: +           - containerPort: 2456 +             protocol: UDP +           - containerPort: 2457 +             protocol: UDP +           - containerPort: 9001 +             protocol: TCP +           - containerPort: 80 +             protocol: TCP +         volumeMounts: +           - name: valheim-data +             mountPath: /opt/valheim +           - name: valheim-config +             mountPath: /config +     volumes: +       - name: valheim-data +         persistentVolumeClaim: +           claimName: valheim-pvc +       - name: valheim-config +         persistentVolumeClaim: +           claimName: valheim-config-pvc +# apps/prod/valheim/ingress.yaml +apiVersion: traefik.io/v1alpha1 +kind: IngressRouteUDP +metadata: + name: valheim-udp1 +spec: + # By default, IngressRouteUDP listens to all UDP entry points if 'entryPoints' is not specified. + entryPoints: +   - valheim-udp1 + routes: +   - services: +       - name: valheim +         port: 2456 # Replace with your service port +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRouteUDP +metadata: + name: valheim-udp2 +spec: + # By default, IngressRouteUDP listens to all UDP entry points if 'entryPoints' is not specified. + entryPoints: +   - valheim-udp2 + routes: +   - services: +       - name: valheim +         port: 2457 # Replace with your service port +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRouteTCP +metadata: + name: valheim-tcp1 +spec: + # By default, IngressRouteUDP listens to all UDP entry points if 'entryPoints' is not specified. + entryPoints: +   - valheim-tcp1 + routes: +   - match: HostSNI(`*`) +     services: +       - name: valheim +         port: 2456 # Replace with your service port +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRouteTCP +metadata: + name: valheim-tcp2 +spec: + # By default, IngressRouteUDP listens to all UDP entry points if 'entryPoints' is not specified. + entryPoints: +   - valheim-tcp2 + routes: +   - match: HostSNI(`*`) +     services: +       - name: valheim +         port: 2457 # Replace with your service port +# apps/prod/valheim/kustomization.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: + - ./namespace.yaml + - ./pvc.yaml + - ./deployment.yaml + - ./service.yaml + - ./ingress.yaml + - ./pv.yaml +patches: + - patch: | +     - op: replace +       path: /metadata/namespace +       value: valheim +   target: +     name: ".*" +# apps/prod/valheim/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: valheim +# apps/prod/valheim/pvc.yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: valheim-pvc + namespace: valheim +spec: + storageClassName: local-path + accessModes: +   - ReadWriteOnce + resources: +   requests: +     storage: 100Gi + volumeName: valheim-pv +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: valheim-config-pvc + namespace: valheim +spec: + storageClassName: local-path + accessModes: +   - ReadWriteOnce + resources: +   requests: +     storage: 100Gi + volumeName: valheim-config-pv +# apps/prod/valheim/pv.yaml +apiVersion: v1 +kind: PersistentVolume +metadata: + name: valheim-pv +spec: + capacity: +   storage: 100Gi + volumeMode: Filesystem + accessModes: +   - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: local-path + claimRef: +   namespace: valheim +   name: valheim-pvc + local: +   path: /var/lib/rancher/k3s/storage/valheim-pvc + nodeAffinity: +   required: +     nodeSelectorTerms: +       - matchExpressions: +           - key: kubernetes.io/hostname +             operator: In +             values: +               - machop +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: valheim-config-pv +spec: + capacity: +   storage: 100Gi + volumeMode: Filesystem + accessModes: +   - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: local-path + claimRef: +   namespace: valheim +   name: valheim-config-pvc + local: +   path: /var/lib/rancher/k3s/storage/valheim-config-pvc + nodeAffinity: +   required: +     nodeSelectorTerms: +       - matchExpressions: +           - key: kubernetes.io/hostname +             operator: In +             values: +               - machop +# apps/prod/valheim/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: valheim + namespace: valheim +spec: + selector: +   app: valheim + ports: +   - name: udp-2456 +     protocol: UDP +     port: 2456 +     targetPort: 2456 +   - name: udp-2457 +     protocol: UDP +     port: 2457 +     targetPort: 2457 +   - name: tcp-2456 +     protocol: TCP +     port: 2456 +     targetPort: 2456 +   - name: tcp-2457 +     protocol: TCP +     port: 2457 +     targetPort: 2457 +``` +I did have to make some changes to k3s configuration to open the correct ports: +``` +charles@machop:~/p3/palworld-arm64$ sudo cat /var/lib/rancher/k3s/server/manifests/traefik-config.yaml +apiVersion: helm.cattle.io/v1 +kind: HelmChartConfig +metadata: + name: traefik + namespace: kube-system +spec: + valuesContent: |- +   ports: +     websecure: +       transport: +         respondingTimeouts: +           readTimeout: 180m +     valheim-udp1: +       port: 2456 +       hostPort: 2456 +       protocol: UDP +     valheim-udp2: +       port: 2457 +       hostPort: 2457 +       protocol: UDP +     valheim-tcp1: +       port: 2456 +       hostPort: 2456 +       protocol: TCP +     valheim-tcp2: +       port: 2457 +       hostPort: 2457 +       protocol: TCP +``` +## Does it work? +Yeah, it seems to work. I had some trouble with crossplay, but I have no interest in playing with console peasants anyway. + +Happy hunting! \ No newline at end of file