Repository: IgnisDa/ryot (language: rust)

Issue:
Ryot crash since 6.9.3
Hi,
until version 6.9.2 everything was working fine for me, but since 6.9.3 I'm getting:

```
[backend] /bin/sh: 1: /usr/local/bin/ryot: Exec format error
[backend] BACKEND_PORT=5000 /usr/local/bin/ryot exited with code 126
--> Sending SIGTERM to other processes..
node:events:492
      throw er; // Unhandled 'error' event
      ^

Error: spawn ps ENOENT
    at ChildProcess._handle.onexit (node:internal/child_process:286:19)
    at onErrorNT (node:internal/child_process:484:16)
    at process.processTicksAndRejections (node:internal/process/task_queues:82:21)
Emitted 'error' event on ChildProcess instance at:
    at ChildProcess._handle.onexit (node:internal/child_process:292:12)
    at onErrorNT (node:internal/child_process:484:16)
    at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
  errno: -2,
  code: 'ENOENT',
  syscall: 'spawn ps',
  path: 'ps',
  spawnargs: [ '-o', 'pid', '--no-headers', '--ppid', 14 ]
}

Node.js v20.10.0
[backend] /bin/sh: 1: /usr/local/bin/ryot: Exec format error
[backend] BACKEND_PORT=5000 /usr/local/bin/ryot exited with code 126
--> Sending SIGTERM to other processes..
node:events:492
      throw er; // Unhandled 'error' event
      ^

Error: spawn ps ENOENT
    at ChildProcess._handle.onexit (node:internal/child_process:286:19)
    at onErrorNT (node:internal/child_process:484:16)
    at process.processTicksAndRejections (node:internal/process/task_queues:82:21)
Emitted 'error' event on ChildProcess instance at:
    at ChildProcess._handle.onexit (node:internal/child_process:292:12)
    at onErrorNT (node:internal/child_process:484:16)
    at process.processTicksAndRejections (node:internal/process/task_queues:82:21) {
  errno: -2,
  code: 'ENOENT',
  syscall: 'spawn ps',
  path: 'ps',
  spawnargs: [ '-o', 'pid', '--no-headers', '--ppid', 14 ]
}

Node.js v20.10.0
```

Maybe it's an issue with the ARM version?


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000000..ec24ae0c33
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,288 @@
+name: Main
+
+on:
+  push:
+    branches: ["main"]
+    tags:
+      - "*-?v[0-9]+*"
+  pull_request:
+    branches: ["main"]
+    types: [opened, synchronize]
+
+env:
+  GHCR_REGISTRY: ghcr.io
+
+permissions:
+  contents: write
+  packages: write
+
+jobs:
+  pre-workflow-checks:
+    runs-on: ubuntu-latest
+    outputs:
+      should-run: ${{ steps.check.outputs.should-run }}
+      should-release: ${{ steps.tag.outputs.should-release }}
+
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          fetch-depth: 2
+      - name: Check commit message
+        id: check
+        run: |
+          if [[ "${{ github.event_name }}" == "push" ]]; then
+            echo "should-run=true" >> $GITHUB_OUTPUT
+          elif [[ "${{ github.event_name }}" == "pull_request" ]]; then
+            COMMIT_MSG=$(git log --format=%B ${{ github.event.pull_request.head.sha }})
+            if [[ "$COMMIT_MSG" == *"Run CI"* ]]; then
+              echo "should-run=true" >> $GITHUB_OUTPUT
+            else
+              echo "should-run=false" >> $GITHUB_OUTPUT
+            fi
+          else
+            echo "should-run=false" >> $GITHUB_OUTPUT
+          fi
+      - name: Check tag
+        id: tag
+        run: |
+          if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == "refs/tags/${{ github.ref_name }}" ]]; then
+            echo "should-release=true" >> $GITHUB_OUTPUT
+          else
+            echo "should-release=false" >> $GITHUB_OUTPUT
+          fi
+
+  create-release:
+    needs:
+      - pre-workflow-checks
+    if: needs.pre-workflow-checks.outputs.should-release == 'true'
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+      - name: Generate changelog
+        id: changelog
+        uses: metcalfc/changelog-generator@v4.1.0
+        with:
+          myToken: ${{ secrets.GITHUB_TOKEN }}
+      - name: Create Release
+        uses: softprops/action-gh-release@v1
+        with:
+          body: |
+            ## What's Changed
+            ${{ steps.changelog.outputs.changelog }}
+          draft: false
+          prerelease: false
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+  build-transactional:
+    needs: pre-workflow-checks
+    if: needs.pre-workflow-checks.outputs.should-run == 'true'
+    runs-on: ubuntu-latest
+    env:
+      MOON_TOOLCHAIN_FORCE_GLOBALS: true
+
+    steps:
+      - uses: actions/checkout@v4
+      - uses: moonrepo/setup-toolchain@v0
+        with:
+          auto-install: true
+      - name: Set up Node.js and caching
+        uses: actions/setup-node@v3
+        with:
+          node-version: '20'
+          cache: 'yarn'
+      - name: Build emails
+        run: moon run transactional:build
+      - name: Upload templates artifact
+        uses: actions/upload-artifact@v4
+        with:
+          name: templates
+          path: |
+            apps/backend/templates/
+          retention-days: 1
+
+  build-backend:
+    needs:
+      - pre-workflow-checks
+      - build-transactional
+    if: needs.pre-workflow-checks.outputs.should-run == 'true'
+    runs-on: ubuntu-latest
+
+    strategy:
+      fail-fast: false
+      matrix:
+        platform:
+          - target: x86_64-unknown-linux-gnu
+            command: cargo
+          - target: aarch64-unknown-linux-gnu
+            command: cross
+
+    steps:
+      - uses: actions/checkout@v4
+      - uses: actions/download-artifact@v4
+        with:
+          name: templates
+          path: ${{ github.workspace }}/apps/backend/templates/
+      - name: Extract build information
+        id: build
+        env:
+          TARGET: ${{ matrix.platform.target }}
+        run: |
+          echo "version=${GITHUB_REF##*/}" >> "$GITHUB_OUTPUT"
+          echo "docker-arch=${{ startsWith(matrix.platform.target, 'x86_64') && 'amd64' || 'arm64' }}" >> "$GITHUB_OUTPUT"
+      - name: Extract rust toolchain
+        id: toolchain
+        run: |
+          echo "channel=$(grep channel rust-toolchain.toml | awk -F' = ' '{printf $2}' | tr -d '\"')" >> "$GITHUB_OUTPUT"
+      - uses: dtolnay/rust-toolchain@master
+        with:
+          toolchain: ${{ steps.toolchain.outputs.channel }}
+          targets: ${{ matrix.platform.target }}
+      - uses: Swatinem/rust-cache@v2
+        with:
+          key: ${{ matrix.platform.target }}-${{ steps.build.outputs.profile }}
+          save-if: ${{ github.event_name != 'pull_request' }}
+      - name: Install cross
+        if: ${{ matrix.platform.command == 'cross' }}
+        uses: taiki-e/cache-cargo-install-action@v2
+        with:
+          tool: cross
+          git: https://github.com/cross-rs/cross
+          rev: 19be83481fd3e50ea103d800d72e0f8eddb1c90c
+          locked: false
+      - name: Build
+        env:
+          APP_VERSION: ${{ steps.build.outputs.version }}
+          DEFAULT_TMDB_ACCESS_TOKEN: ${{ secrets.DEFAULT_TMDB_ACCESS_TOKEN }}
+          DEFAULT_MAL_CLIENT_ID: ${{ secrets.DEFAULT_MAL_CLIENT_ID }}
+          DEFAULT_GOOGLE_BOOKS_API_KEY: ${{ secrets.DEFAULT_GOOGLE_BOOKS_API_KEY }}
+        run: |
+          ${{ matrix.platform.command }} build --locked --target ${{ matrix.platform.target }} --release
+      - name: Upload build artifact
+        uses: actions/upload-artifact@v4
+        with:
+          name: backend-${{ steps.build.outputs.docker-arch }}
+          path: ${{ github.workspace }}/target/${{ matrix.platform.target }}/release/ryot
+          retention-days: 1
+
+  build-docker:
+    needs:
+      - pre-workflow-checks
+      - build-backend
+    if: needs.pre-workflow-checks.outputs.should-run == 'true'
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v4
+      - name: Download build artifact for docker
+        uses: actions/download-artifact@v4
+        with:
+          path: ${{ github.workspace }}/artifact/
+      - 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 the ghcr container registry
+        uses: docker/login-action@v3
+        with:
+          registry: ${{ env.GHCR_REGISTRY }}
+          username: ${{ github.repository_owner }}
+          password: ${{ secrets.GITHUB_TOKEN }}
+      - name: Log in to the docker hub container registry
+        uses: docker/login-action@v3
+        with:
+          username: ${{ secrets.DOCKER_USERNAME }}
+          password: ${{ secrets.DOCKER_TOKEN }}
+      - name: Extract metadata for Docker
+        id: meta
+        uses: docker/metadata-action@v5
+        with:
+          images: |
+            name=${{ secrets.DOCKER_USERNAME }}/${{ github.event.repository.name }},enable={{is_default_branch}}
+            name=${{ env.GHCR_REGISTRY }}/${{ github.repository_owner }}/${{ github.event.repository.name }}
+          tags: |
+            type=ref,event=pr
+            type=semver,pattern=v{{version}},enable={{is_default_branch}}
+            type=semver,pattern=v{{major}}.{{minor}},enable={{is_default_branch}}
+            type=semver,pattern=v{{major}},enable={{is_default_branch}}
+            type=raw,value=develop,enable={{is_default_branch}}
+      - name: Build and push
+        uses: docker/build-push-action@v5
+        with:
+          context: .
+          push: true
+          cache-from: type=gha
+          cache-to: type=gha,mode=max
+          platforms: linux/amd64,linux/arm64
+          tags: ${{ steps.meta.outputs.tags }}
+
+  upload-kodi-plugin:
+    needs:
+      - pre-workflow-checks
+      - build-docker
+    if: needs.pre-workflow-checks.outputs.should-release == 'true'
+    runs-on: ubuntu-20.04
+    env:
+      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      MOON_TOOLCHAIN_FORCE_GLOBALS: true
+
+    steps:
+      - uses: actions/checkout@v4
+      - name: Setup Moon
+        uses: moonrepo/setup-toolchain@v0
+        with:
+          auto-install: true
+      - name: Set up Node.js and caching
+        uses: actions/setup-node@v3
+        with:
+          node-version: '20'
+          cache: 'yarn'
+      - name: Build plugin
+        run: moon run kodi:build
+      - name: Upload plugin to releases
+        run: gh release upload --clobber ${{ github.ref_name }} "tmp/script.ryot.zip"
+
+  deploy-demo-instance:
+    needs:
+      - pre-workflow-checks
+      - build-docker
+    if: needs.pre-workflow-checks.outputs.should-release == 'true'
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v4
+      - name: Set up CLI
+        uses: superfly/flyctl-actions/setup-flyctl@master
+      - name: Deploy
+        run: flyctl deploy --remote-only --detach --config ci/fly.toml
+        env:
+          FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
+
+  deploy-docs:
+    needs:
+      - pre-workflow-checks
+      - build-docker
+    if: needs.pre-workflow-checks.outputs.should-release == 'true'
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v4
+      - uses: actions/setup-python@v5
+        with:
+          python-version: 3.x
+      - name: Install poetry
+        uses: abatilo/actions-poetry@v2
+      - name: Install dependencies
+        run: cd docs && poetry install
+      - name: Build docs
+        run: cd docs && poetry run mkdocs build
+      - name: Push to deployment branch
+        uses: peaceiris/actions-gh-pages@v4
+        with:
+          github_token: ${{ secrets.GITHUB_TOKEN }}
+          publish_dir: docs/site
+          publish_branch: nf-docs
+          force_orphan: true
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
deleted file mode 100644
index 6abdd27791..0000000000
--- a/.github/workflows/release.yml
+++ /dev/null
@@ -1,195 +0,0 @@
-name: Release
-
-on:
-  push:
-    tags:
-      - "*-?v[0-9]+*"
-
-env:
-  GHCR_REGISTRY: ghcr.io
-
-permissions:
-  contents: write
-  packages: write
-
-jobs:
-  create-release:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v4
-
-      - name: Create or update release
-        uses: actions/github-script@v7
-        env:
-          TAG_NAME: ${{ github.ref_name }}
-        with:
-          script: |
-            const tag = process.env.TAG_NAME || github.ref_name;
-            const repo = context.repo;
-            const majorVersion = tag.match(/v(\d+)\.\d+\.\d+/)[1];
-            const releaseName = `Version ${majorVersion}`;
-            const date = new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
-            const dynamicBody = `<!-- DYNAMIC START -->\n*Created from tag*: \`${tag}\`\n*Created from git revision*: \`${context.sha}\`\n*Published on*: \`${date}\`\n<!-- DYNAMIC END -->`;
-
-            async function findOrCreateRelease() {
-              const { data: releases } = await github.rest.repos.listReleases(repo);
-              let existingRelease = releases.find(release => release.name === releaseName);
-
-              if (existingRelease) {
-                const existingBody = existingRelease.body;
-                const newBody = existingBody.replace(/<!-- DYNAMIC START -->[^]*<!-- DYNAMIC END -->/, dynamicBody);
-                await github.rest.repos.updateRelease({
-                  ...repo,
-                  release_id: existingRelease.id,
-                  tag_name: tag,
-                  body: newBody,
-                  draft: false,
-                });
-                console.log("Release updated to associate with new tag.");
-              } else {
-                const fullBody = `${dynamicBody}\n\n## Release Notes\n\n- Some bug fixes.`;
-                await github.rest.repos.createRelease({
-                  ...repo,
-                  tag_name: tag,
-                  name: releaseName,
-                  body: fullBody,
-                  draft: false,
-                  prerelease: false
-                });
-                console.log("Release created successfully.");
-              }
-            }
-
-            findOrCreateRelease();
-
-  docker-release:
-    runs-on: ubuntu-latest
-    needs: create-release
-    steps:
-      - 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 the ghcr container registry
-        uses: docker/login-action@v3
-        with:
-          registry: ${{ env.GHCR_REGISTRY }}
-          username: ${{ github.actor }}
-          password: ${{ secrets.GITHUB_TOKEN }}
-
-      - name: Log in to the docker hub container registry
-        uses: docker/login-action@v3
-        with:
-          username: ${{ secrets.DOCKER_USERNAME }}
-          password: ${{ secrets.DOCKER_TOKEN }}
-
-      - name: Get docker image names
-        id: required_args
-        uses: actions/github-script@v7
-        env:
-          DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
-        with:
-          script: |
-            const repoName = context.payload.repository.name;
-            const refName = context.ref.replace('refs/tags/', '');
-            core.setOutput('APP_VERSION', refName);
-
-            const dockerHubActor = process.env.DOCKER_USERNAME;
-            const ghcrRegistry = process.env.GHCR_REGISTRY;
-            const ghcrActor = context.actor;
-
-            function generateVersionArray(version) {
-              const parts = version.split(".");
-              const versionArray = [];
-              for (let i = 0; i < parts.length; i++)
-                versionArray.push(parts.slice(0, i + 1).join("."));
-              versionArray.push("latest");
-              return versionArray;
-            }
-
-            const versionTags = generateVersionArray(refName);
-            const ghcrImageName = `${ghcrRegistry}/${ghcrActor}/${repoName}`;
-            const ghcrTags = versionTags.map((tag) => `${ghcrImageName}:${tag}`);
-
-            const dockerHubImageName = `${dockerHubActor}/${repoName}`;
-            const dockerHubTags = versionTags.map((tag) => `${dockerHubImageName}:${tag}`);
-
-            const imageNames = [...ghcrTags, dockerHubTags].join(",").toLowerCase();
-            core.setOutput('image_names', imageNames);
-
-      - name: Build and push to ghcr
-        uses: docker/build-push-action@v5
-        with:
-          context: .
-          platforms: linux/amd64,linux/arm64
-          push: true
-          tags: ${{ steps.required_args.outputs.image_names }}
-          build-args: |
-            APP_VERSION=${{ steps.required_args.outputs.APP_VERSION }}
-            DEFAULT_TMDB_ACCESS_TOKEN=${{ secrets.DEFAULT_TMDB_ACCESS_TOKEN }}
-            DEFAULT_MAL_CLIENT_ID=${{ secrets.DEFAULT_MAL_CLIENT_ID }}
-            DEFAULT_GOOGLE_BOOKS_API_KEY=${{ secrets.DEFAULT_GOOGLE_BOOKS_API_KEY }}
-
-  upload-kodi-plugin:
-    runs-on: ubuntu-20.04
-    needs: docker-release
-    env:
-      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-    steps:
-      - uses: actions/checkout@v4
-
-      - name: Setup Moon
-        uses: moonrepo/setup-toolchain@v0
-        with:
-          auto-install: true
-
-      - name: Build plugin
-        run: moon run kodi:build
-
-      - name: Upload plugin to releases
-        run: gh release upload --clobber ${{ github.ref_name }} "tmp/script.ryot.zip"
-
-  deploy-demo-instance:
-    runs-on: ubuntu-latest
-    needs: docker-release
-    steps:
-      - uses: actions/checkout@v4
-
-      - name: Set up CLI
-        uses: superfly/flyctl-actions/setup-flyctl@master
-
-      - name: Deploy
-        run: flyctl deploy --remote-only --detach --config ci/fly.toml
-        env:
-          FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
-
-  deploy-docs:
-    runs-on: ubuntu-latest
-    needs: docker-release
-    steps:
-      - uses: actions/checkout@v4
-
-      - uses: actions/setup-python@v5
-        with:
-          python-version: 3.x
-
-      - name: Install poetry
-        uses: abatilo/actions-poetry@v2
-
-      - name: Install dependencies
-        run: cd docs && poetry install
-
-      - name: Build docs
-        run: cd docs && poetry run mkdocs build
-
-      - name: Push to deployment branch
-        uses: peaceiris/actions-gh-pages@v4
-        with:
-          github_token: ${{ secrets.GITHUB_TOKEN }}
-          publish_dir: docs/site
-          publish_branch: nf-docs
-          force_orphan: true
diff --git a/Cross.toml b/Cross.toml
new file mode 100644
index 0000000000..d1136ace5b
--- /dev/null
+++ b/Cross.toml
@@ -0,0 +1,10 @@
+[build.env]
+passthrough = [
+    "APP_VERSION",
+    "DEFAULT_TMDB_ACCESS_TOKEN",
+    "DEFAULT_MAL_CLIENT_ID",
+    "DEFAULT_GOOGLE_BOOKS_API_KEY",
+]
+
+[build]
+pre-build = "ci/cross-pre-build.sh"
diff --git a/Dockerfile b/Dockerfile
index 679f77e850..03d71c1196 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,8 +1,6 @@
 ARG NODE_BASE_IMAGE=node:20.10.0-bookworm-slim
 
-FROM --platform=$BUILDPLATFORM $NODE_BASE_IMAGE AS node-build-base
-
-FROM node-build-base AS frontend-build-base
+FROM $NODE_BASE_IMAGE AS frontend-build-base
 ENV MOON_TOOLCHAIN_FORCE_GLOBALS=true
 WORKDIR /app
 RUN apt update && apt install -y --no-install-recommends git curl ca-certificates xz-utils
@@ -11,63 +9,41 @@ RUN npm install -g @moonrepo/cli && moon --version
 FROM frontend-build-base AS frontend-workspace
 WORKDIR /app
 COPY . .
-RUN moon docker scaffold frontend transactional
+RUN moon docker scaffold frontend
 
 FROM frontend-build-base AS frontend-builder
 WORKDIR /app
 COPY --from=frontend-workspace /app/.moon/docker/workspace .
 RUN moon docker setup
 COPY --from=frontend-workspace /app/.moon/docker/sources .
-RUN moon run frontend:build transactional:build
+RUN moon run frontend:build
 RUN moon docker prune
 
-FROM --platform=$BUILDPLATFORM lukemathwalker/cargo-chef AS backend-chef
-RUN apt-get update && apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross clang llvm ca-certificates pkg-config make g++ libssl-dev
-RUN update-ca-certificates
-WORKDIR app
-
-FROM backend-chef AS backend-planner
-COPY . .
-RUN cargo chef prepare --recipe-path recipe.json
-
-FROM backend-chef AS backend-builder
-# build specific
-ARG BUILD_PROFILE=release
-# application specific
-ARG APP_VERSION
-ARG DEFAULT_TMDB_ACCESS_TOKEN
-ARG DEFAULT_MAL_CLIENT_ID
-ARG DEFAULT_GOOGLE_BOOKS_API_KEY
-RUN test -n "$APP_VERSION" && \
-    test -n "$DEFAULT_TMDB_ACCESS_TOKEN" && \
-    test -n "$DEFAULT_MAL_CLIENT_ID" && \
-    test -n "$DEFAULT_GOOGLE_BOOKS_API_KEY"
-COPY --from=backend-planner /app/recipe.json recipe.json
-RUN cargo chef cook --profile $BUILD_PROFILE --recipe-path recipe.json
-COPY . .
-COPY --from=frontend-builder /app/apps/backend/templates ./apps/backend/templates
-RUN APP_VERSION=$APP_VERSION \
-    DEFAULT_TMDB_ACCESS_TOKEN=$DEFAULT_TMDB_ACCESS_TOKEN \
-    DEFAULT_MAL_CLIENT_ID=$DEFAULT_MAL_CLIENT_ID \
-    DEFAULT_GOOGLE_BOOKS_API_KEY=$DEFAULT_GOOGLE_BOOKS_API_KEY \
-    cargo build --profile ${BUILD_PROFILE} --bin ryot
-RUN cp -R /app/target/${BUILD_PROFILE}/ryot /app/ryot
+FROM --platform=${BUILDPLATFORM} alpine as artifact
+COPY artifact/ /artifact/
+ARG TARGETARCH
+ENV TARGETARCH=${TARGETARCH}
+RUN mv /artifact/backend-${TARGETARCH}/ryot /artifact/ryot
+RUN chmod +x /artifact/ryot
 
 FROM $NODE_BASE_IMAGE
+ARG TARGETARCH
+ENV TARGETARCH=${TARGETARCH}
 LABEL org.opencontainers.image.source="https://github.com/IgnisDa/ryot"
 ENV FRONTEND_UMAMI_SCRIPT_URL="https://umami.diptesh.me/script.js"
 ENV FRONTEND_UMAMI_WEBSITE_ID="5ecd6915-d542-4fda-aa5f-70f09f04e2e0"
 COPY --from=caddy:2.7.5 /usr/bin/caddy /usr/local/bin/caddy
-RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates procps && rm -rf /var/lib/apt/lists/*
+RUN apt-get update && apt-get install -y --no-install-recommends libssl3 ca-certificates procps && rm -rf /var/lib/apt/lists/*
 RUN npm install --global concurrently@8.2.2 && concurrently --version
 RUN useradd -m -u 1001 ryot
+RUN if [ "${TARGETARCH}" = "arm64" ]; then apt-get update && apt-get install -y --no-install-recommends wget && wget http://ftp.debian.org/debian/pool/main/o/openssl/libssl1.1_1.1.1w-0+deb11u1_arm64.deb && dpkg -i libssl1.1_1.1.1w-0+deb11u1_arm64.deb && rm -rf libssl1.1_1.1.1w-0+deb11u1_arm64.deb && apt-get remove -y wget && rm -rf  rm -rf /var/lib/apt/lists/*; fi
 WORKDIR /home/ryot
 USER ryot
 COPY ci/Caddyfile /etc/caddy/Caddyfile
 COPY --from=frontend-builder --chown=ryot:ryot /app/apps/frontend/node_modules ./node_modules
 COPY --from=frontend-builder --chown=ryot:ryot /app/apps/frontend/package.json ./package.json
 COPY --from=frontend-builder --chown=ryot:ryot /app/apps/frontend/build ./build
-COPY --from=backend-builder --chown=ryot:ryot /app/ryot /usr/local/bin/ryot
+COPY --from=artifact --chown=ryot:ryot /artifact/ryot /usr/local/bin/ryot
 CMD [ \
     "concurrently", "--names", "frontend,backend,proxy", "--kill-others", \
     "PORT=3000 npx remix-serve ./build/server/index.js", \
diff --git a/ci/cross-pre-build.sh b/ci/cross-pre-build.sh
new file mode 100644
index 0000000000..3b15d0e343
--- /dev/null
+++ b/ci/cross-pre-build.sh
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+set -euxo pipefail
+
+if [[ "$CROSS_TARGET" == "aarch64-unknown-linux-gnu" ]]; then
+  dpkg --add-architecture $CROSS_DEB_ARCH
+  apt-get update && apt-get --assume-yes install libssl-dev:$CROSS_DEB_ARCH
+fi


Test patch: