feat(deploy): container image and K3s manifest for the web build

Dockerfile builds the wasm bundle and papercraft-relay, which serves the app, the
agent relay and shared projects (--data on a PV). craft-libs is a private git
dependency, so the build takes host-vendored crates as a build context.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Marcos Paulo
2026-10-14 09:39:05 -03:00
parent 0b78bb1d85
commit 5f667a5ae9
3 changed files with 124 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
target/
analysis/
.git/
.worktrees/
web/pkg/
dist/
packaging/
+32
View File
@@ -0,0 +1,32 @@
# Browser build + relay in one image: `papercraft-relay --static` serves web/ and the agent relay.
# craft-libs is a private Gitea dependency, so vendor on the host (authenticated) and pass it in:
# cargo vendor --locked /path/vendor/crates >/dev/null
# docker build -f deploy/Dockerfile --build-context vendor=/path/vendor -t localhost:32000/papercraft-web:<tag> .
FROM rust:slim AS build
# Must match the wasm-bindgen crate in Cargo.lock.
ARG WASM_BINDGEN=0.2.129
RUN rustup target add wasm32-unknown-unknown \
&& apt-get update && apt-get install -y --no-install-recommends binaryen pkg-config libssl-dev \
&& rm -rf /var/lib/apt/lists/* \
&& cargo install wasm-bindgen-cli --version ${WASM_BINDGEN} --locked
COPY --from=vendor crates /vendor
RUN printf '%s\n' \
'[source.crates-io]' 'replace-with = "vendored-sources"' \
'[source."git+https://git.marcospaulo.dev.br/masi/craft-libs?rev=22544b6d619d4f16779b8bdfa3f0b642a86fc065"]' \
'git = "https://git.marcospaulo.dev.br/masi/craft-libs"' \
'rev = "22544b6d619d4f16779b8bdfa3f0b642a86fc065"' 'replace-with = "vendored-sources"' \
'[source.vendored-sources]' 'directory = "/vendor"' > "$CARGO_HOME/config.toml"
WORKDIR /src
COPY . .
RUN sh web/build.sh && cargo build --release --locked --bin papercraft-relay
FROM debian:stable-slim
RUN apt-get update && apt-get install -y --no-install-recommends libssl3 ca-certificates \
&& rm -rf /var/lib/apt/lists/* \
&& useradd --system --uid 10001 papercraft
COPY --from=build /src/target/release/papercraft-relay /usr/local/bin/papercraft-relay
COPY --from=build /src/web/index.html /srv/web/index.html
COPY --from=build /src/web/pkg /srv/web/pkg
USER 10001
EXPOSE 8787
ENTRYPOINT ["papercraft-relay", "--static", "/srv/web", "--data", "/data"]
+85
View File
@@ -0,0 +1,85 @@
# Shared projects live in /data (`--data`) on the HDD: PV /srv/hdd/papercraft, class hdd-storage, Retain (same pattern as actual, kavita, ...).
# Anyone holding a share link can read and edit that project; the Tailnet-only ingress is the outer boundary.
# Tailnet access (DNS, TLS, ingress) is added with `expose papercraft/papercraft papercraft`; see deploy/README.md.
apiVersion: v1
kind: Namespace
metadata:
name: papercraft
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-hdd-papercraft
spec:
capacity: {storage: 5Gi}
accessModes: [ReadWriteOnce]
persistentVolumeReclaimPolicy: Retain
storageClassName: hdd-storage
volumeMode: Filesystem
local: {path: /srv/hdd/papercraft}
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- {key: kubernetes.io/hostname, operator: In, values: [kubernets]}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: papercraft-data
namespace: papercraft
spec:
accessModes: [ReadWriteOnce]
storageClassName: hdd-storage
volumeName: pv-hdd-papercraft
resources:
requests: {storage: 5Gi}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: papercraft
namespace: papercraft
spec:
replicas: 1
selector:
matchLabels: {app: papercraft}
template:
metadata:
labels: {app: papercraft}
spec:
securityContext: {fsGroup: 10001}
volumes:
- name: data
persistentVolumeClaim: {claimName: papercraft-data}
containers:
- name: web
image: localhost:32000/papercraft-web:0.2.0-110c99d-wgsl
args: ["--allow-origin", "https://papercraft.marcospaulo.dev.br"]
volumeMounts:
- {name: data, mountPath: /data}
ports:
- containerPort: 8787
readinessProbe:
httpGet: {path: /index.html, port: 8787}
livenessProbe:
httpGet: {path: /index.html, port: 8787}
initialDelaySeconds: 10
resources:
requests: {cpu: 20m, memory: 32Mi}
limits: {cpu: 500m, memory: 256Mi}
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: {drop: [ALL]}
---
apiVersion: v1
kind: Service
metadata:
name: papercraft
namespace: papercraft
spec:
selector: {app: papercraft}
ports:
- port: 80
targetPort: 8787