diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml
new file mode 100644
index 0000000..14bc5f3
--- /dev/null
+++ b/.forgejo/workflows/build.yaml
@@ -0,0 +1,126 @@
+on:
+  push:
+    tags:
+      - "**"
+  workflow_dispatch:
+    inputs:
+      verbose:
+        description: "Verbose"
+        required: false
+        default: false
+        type: boolean
+
+jobs:
+  test:
+    runs-on: container
+    container:
+      image: code.philo.ydns.eu/philorg/gisaf-backend-ci
+      volumes:
+        - "uv_cache:/root/.cache/uv"
+        - "ca-cert:/etc/containers/certs.d"
+    services:
+      gisaf-database:
+        image: code.philo.ydns.eu/philorg/gisaf-database
+      redis:
+        image: docker.io/redis:alpine
+    steps:
+      - name: Echo env
+        if: ${{ inputs.verbose }}
+        run: |
+          echo '${{ toJSON(env) }}'
+
+      - uses: actions/checkout@v4
+
+      - name: Install app with 'uv pip install'
+        run: uv pip install --python=$UV_PROJECT_ENVIRONMENT --no-deps .
+
+      - name: Initialize database
+        run: GISAF__DB__HOST=gisaf-database gisaf create-db
+
+      - name: Run tests (API call)
+        run: GISAF__DB__HOST=gisaf-database GISAF__GISAF_LIVE__REDIS=redis://redis pytest -s tests/basic.py -W ignore::DeprecationWarning
+
+  build:
+    runs-on: container
+    needs: test
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+
+      - name: Get the version from git
+        id: version
+        run: echo "version=$(git describe --dirty --tags)" >> $GITHUB_OUTPUT
+
+      - name: Check if the container should be built
+        id: builder
+        env:
+          RUN: ${{ toJSON(inputs.build || !contains(steps.version.outputs.version, '-g')) }}
+        run: |
+          echo "run=$RUN" >> $GITHUB_OUTPUT
+          echo "Run build: $RUN"
+
+      - name: Info - version and test if the git version is clean (then python package and image container should be built)
+        env:
+          VERSION: ${{ steps.version.outputs.version }}
+          RUN: ${{ steps.builder.outputs.run }}
+          FORCE: ${{ toJSON(inputs.build) }}
+        run: |
+          echo "Version $VERSION, force (manual input): $FORCE, run the build: $RUN"
+
+      - name: Set the version in pyproject.toml (workaround for uv not supporting dynamic version)
+        if: fromJSON(steps.builder.outputs.run)
+        env:
+          VERSION: ${{ steps.version.outputs.version }}
+        run: sed "s/0.0.0/${VERSION}/" -i pyproject.toml
+
+      - name: Workaround for bug of podman-login
+        if: fromJSON(steps.builder.outputs.run)
+        run: |
+          mkdir -p $HOME/.docker
+          echo "{ \"auths\": {} }" > $HOME/.docker/config.json
+
+      - name: Log in to the container registry (with another workaround)
+        if: fromJSON(steps.builder.outputs.run)
+        uses: actions/podman-login@v1
+        with:
+          registry: ${{ vars.REGISTRY }}
+          username: ${{ secrets.REGISTRY_USER }}
+          password: ${{ secrets.REGISTRY_PASSWORD }}
+          auth_file_path: /tmp/auth.json
+
+      - name: Build the container image
+        if: fromJSON(steps.builder.outputs.run)
+        uses: actions/buildah-build@v1
+        with:
+          image: gisaf-backend
+          oci: true
+          labels: gisaf-backend
+          tags: latest ${{ steps.version.outputs.version }}
+          containerfiles: |
+            ./Containerfile
+          build-args: |
+            APP_VERSION=${{ steps.version.outputs.version }}
+
+      - name: Push the image to the registry
+        if: fromJSON(steps.builder.outputs.run)
+        uses: actions/push-to-registry@v2
+        with:
+          registry: "docker://${{ vars.REGISTRY }}/${{ vars.ORGANISATION }}"
+          image: gisaf-backend
+          tags: latest ${{ steps.version.outputs.version }}
+
+      - name: Install uv
+        uses: astral-sh/setup-uv@v4
+        with:
+          version: "0.5.9"
+
+      - name: Build python package
+        if: fromJSON(steps.builder.outputs.run)
+        run: uv build --wheel
+
+      - name: Publish Python package (home)
+        if: fromJSON(steps.builder.outputs.run)
+        env:
+          LOCAL_PYPI_TOKEN: ${{ secrets.LOCAL_PYPI_TOKEN }}
+        run: uv publish --publish-url https://code.philo.ydns.eu/api/packages/philorg/pypi --token $LOCAL_PYPI_TOKEN