diff --git a/.drone.yml b/.drone.yml
deleted file mode 100644
index 3fc571a..0000000
--- a/.drone.yml
+++ /dev/null
@@ -1,176 +0,0 @@
-kind: pipeline
-type: docker
-name: build-release-images
-trigger:
-  event:
-    exclude:
-    - pull_request
-  ref:
-    - refs/tags/**
-
-platform:
-  os: linux
-  arch: amd64
-
-steps:
-
-- name: get-tags
-  image: docker:git
-  commands:
-    - git fetch --tags
-  depends_on:
-    - clone
-
-- name: make-tags
-  image: node
-  commands:
-    - echo -n "${DRONE_TAG}, latest" > .tags
-  depends_on:
-    - get-tags
-
-- name: build-gitea
-  image: thegeeklab/drone-docker-buildx
-  privileged: true
-  settings:
-    provenance: false
-    registry: git.mrmeeb.stream
-    username: 
-      from_secret: docker_username
-    password: 
-      from_secret: docker_password
-    repo: git.mrmeeb.stream/mrmeeb/certbot-cron
-    platforms:
-      - linux/arm64
-      - linux/amd64
-  depends_on:
-    - make-tags
-
-- name: release-gitea
-  image: goreleaser/goreleaser
-  environment:
-    GITEA_TOKEN:
-      from_secret: gitea_token
-  commands:
-    - goreleaser release -f .goreleaser-gitea.yaml
-  depends_on:
-    - build-gitea
-
-- name: notify
-  image: plugins/slack
-  when:
-    status:
-    - success
-    - failure
-  settings:
-    webhook: 
-      from_secret: slack_webhook
-  depends_on:
-    - release-gitea
-
----
-kind: pipeline
-type: docker
-name: build-main-images
-trigger:
-  event:
-    exclude:
-    - pull_request
-  ref:
-    - refs/heads/master
-
-platform:
-  os: linux
-  arch: amd64
-
-steps:
-
-- name: make-tags
-  image: node
-  commands:
-    - echo -n "${DRONE_COMMIT_SHA:0:8}, latest" > .tags
-  when:
-    ref:
-      - refs/heads/master
-  depends_on:
-    - clone
-
-- name: build-gitea
-  image: thegeeklab/drone-docker-buildx
-  privileged: true
-  settings:
-    provenance: false
-    registry: git.mrmeeb.stream
-    username: 
-      from_secret: docker_username
-    password: 
-      from_secret: docker_password
-    repo: git.mrmeeb.stream/mrmeeb/certbot-cron
-    platforms:
-      - linux/arm64
-      - linux/amd64
-  depends_on:
-    - make-tags
-
-- name: notify
-  image: plugins/slack
-  when:
-    status:
-    - success
-    - failure
-  settings:
-    webhook: 
-      from_secret: slack_webhook
-  depends_on:
-    - build-gitea
-
----
-kind: pipeline
-type: docker
-name: build-develop-images
-trigger:
-  event:
-    exclude:
-    - pull_request
-  branch:
-    - develop
-
-platform:
-  os: linux
-  arch: amd64
-
-steps:
-  # Set tags for develop branch - git commit SHA and 'develop'
-- name: make-tags
-  image: node
-  commands:
-    - echo -n "develop-${DRONE_COMMIT_SHA:0:8}, develop" > .tags
-   
-  # Build containers from develop branch
-- name: build-gitea
-  image: thegeeklab/drone-docker-buildx
-  privileged: true
-  settings:
-    provenance: false
-    registry: git.mrmeeb.stream
-    username: 
-      from_secret: docker_username
-    password: 
-      from_secret: docker_password
-    repo: git.mrmeeb.stream/mrmeeb/certbot-cron
-    platforms:
-      - linux/arm64
-      - linux/amd64
-  depends_on:
-    - make-tags
-
-- name: notify
-  image: plugins/slack
-  when:
-    status:
-    - success
-    - failure
-  settings:
-    webhook: 
-      from_secret: slack_webhook
-  depends_on:
-    - build-gitea
\ No newline at end of file
diff --git a/.gitea/workflows/build-develop.yaml b/.gitea/workflows/build-develop.yaml
new file mode 100644
index 0000000..83c5a59
--- /dev/null
+++ b/.gitea/workflows/build-develop.yaml
@@ -0,0 +1,88 @@
+name: Build Image
+on:
+  push:
+    branches:
+      - 'develop'
+
+env:
+  TEST_TAG: mrmeeb/certbot-cron:test
+  FULL_TAG: git.mrmeeb.stream/mrmeeb/certbot-cron:develop
+
+jobs:
+  "Validate Image":
+    runs-on: [ubuntu-docker-latest, linux/amd64]
+    steps:
+      - name: Build locally
+        uses: docker/build-push-action@v5
+        with:
+          load: true
+          tags: ${{ env.TEST_TAG }}
+          provenance: false
+      - name: Test certificate issuing
+        id: test
+        run: |
+          # First create a volume
+          docker volume create ${{ gitea.sha }} && \
+          # Then issue a certificate
+          docker run --rm -v ${{ gitea.sha }}:/config -e STAGING=true -e EMAIL=${{ secrets.EMAIL }} -e DOMAINS=${{ gitea.sha }}.mrmeeb.stream -e PLUGIN=cloudflare -e CLOUDFLARE_TOKEN=${{ secrets.CLOUDFLARE_TOKEN }} -e ONE_SHOT=true -e GENERATE_DHPARAM=false ${{ env.TEST_TAG }} && \
+          # Then revoke it again
+          docker run --rm --entrypoint "/usr/bin/certbot" -v ${{ gitea.sha }}:/config ${{ env.TEST_TAG }} revoke --non-interactive --agree-tos --email ${{ secrets.EMAIL }} --staging --config-dir /config/letsencrypt --work-dir /config/.tmp --logs-dir /config/logs --cert-path /config/letsencrypt/live/${{ gitea.sha }}.mrmeeb.stream/fullchain.pem          
+      - name: Tidy up
+        if: always()
+        run: |
+          echo "Removing docker volume ${{ gitea.sha }}" && \
+          docker volume rm ${{ gitea.sha }}          
+      - name: Test Failure
+        uses: rjstone/discord-webhook-notify@v1
+        if: failure()
+        with:
+          severity: error
+          details: Test Failed!
+          webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
+          username: Gitea
+          avatarUrl: ${{ vars.RUNNER_ICON_URL }}
+  
+  "Publish Image":
+    runs-on: [ubuntu-docker-latest, linux/amd64]
+    needs: ["Validate Image"]
+    steps:
+      - name: Set up QEMU
+        uses: docker/setup-qemu-action@v3
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v3
+      - name: Login to Gitea Container Registry
+        uses: docker/login-action@v3
+        with:
+          registry: git.mrmeeb.stream
+          username: ${{ env.GITHUB_ACTOR }}
+          password: ${{ secrets.GTCR_TOKEN }}
+      - name: Build and push
+        uses: docker/build-push-action@v5
+        with:
+          push: true
+          platforms: linux/amd64,linux/arm64
+          tags: ${{ env.FULL_TAG }}
+          provenance: false
+      - name: Build Failure
+        uses: rjstone/discord-webhook-notify@v1
+        if: failure()
+        with:
+          severity: error
+          details: Build Failed!
+          webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
+          username: Gitea
+          avatarUrl: ${{ vars.RUNNER_ICON_URL }}
+
+  "Notify":
+    runs-on: [ubuntu-docker-latest, linux/amd64]
+    needs: ["Build Image", "Validate Image"]
+    steps:
+      - name: Notify of success
+        uses: rjstone/discord-webhook-notify@v1
+        if: success()
+        with:
+          severity: info
+          details: Build succeeded!
+          webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
+          username: Gitea
+          avatarUrl: ${{ vars.RUNNER_ICON_URL }}
\ No newline at end of file
diff --git a/.gitea/workflows/build-main.yaml b/.gitea/workflows/build-main.yaml
new file mode 100644
index 0000000..fca21de
--- /dev/null
+++ b/.gitea/workflows/build-main.yaml
@@ -0,0 +1,88 @@
+name: Build Image
+on:
+  push:
+    branches:
+      - 'main'
+
+env:
+  TEST_TAG: mrmeeb/certbot-cron:test
+  FULL_TAG: git.mrmeeb.stream/mrmeeb/certbot-cron:latest
+
+jobs:
+  "Validate Image":
+    runs-on: [ubuntu-docker-latest, linux/amd64]
+    steps:
+      - name: Build locally
+        uses: docker/build-push-action@v5
+        with:
+          load: true
+          tags: ${{ env.TEST_TAG }}
+          provenance: false
+      - name: Test certificate issuing
+        id: test
+        run: |
+          # First create a volume
+          docker volume create ${{ gitea.sha }} && \
+          # Then issue a certificate
+          docker run --rm -v ${{ gitea.sha }}:/config -e STAGING=true -e EMAIL=${{ secrets.EMAIL }} -e DOMAINS=${{ gitea.sha }}.mrmeeb.stream -e PLUGIN=cloudflare -e CLOUDFLARE_TOKEN=${{ secrets.CLOUDFLARE_TOKEN }} -e ONE_SHOT=true -e GENERATE_DHPARAM=false ${{ env.TEST_TAG }} && \
+          # Then revoke it again
+          docker run --rm --entrypoint "/usr/bin/certbot" -v ${{ gitea.sha }}:/config ${{ env.TEST_TAG }} revoke --non-interactive --agree-tos --email ${{ secrets.EMAIL }} --staging --config-dir /config/letsencrypt --work-dir /config/.tmp --logs-dir /config/logs --cert-path /config/letsencrypt/live/${{ gitea.sha }}.mrmeeb.stream/fullchain.pem          
+      - name: Tidy up
+        if: always()
+        run: |
+          echo "Removing docker volume ${{ gitea.sha }}" && \
+          docker volume rm ${{ gitea.sha }}          
+      - name: Test Failure
+        uses: rjstone/discord-webhook-notify@v1
+        if: failure()
+        with:
+          severity: error
+          details: Test Failed!
+          webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
+          username: Gitea
+          avatarUrl: ${{ vars.RUNNER_ICON_URL }}
+  
+  "Publish Image":
+    runs-on: [ubuntu-docker-latest, linux/amd64]
+    needs: ["Validate Image"]
+    steps:
+      - name: Set up QEMU
+        uses: docker/setup-qemu-action@v3
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v3
+      - name: Login to Gitea Container Registry
+        uses: docker/login-action@v3
+        with:
+          registry: git.mrmeeb.stream
+          username: ${{ env.GITHUB_ACTOR }}
+          password: ${{ secrets.GTCR_TOKEN }}
+      - name: Build and push
+        uses: docker/build-push-action@v5
+        with:
+          push: true
+          platforms: linux/amd64,linux/arm64
+          tags: ${{ env.FULL_TAG }}
+          provenance: false
+      - name: Build Failure
+        uses: rjstone/discord-webhook-notify@v1
+        if: failure()
+        with:
+          severity: error
+          details: Build Failed!
+          webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
+          username: Gitea
+          avatarUrl: ${{ vars.RUNNER_ICON_URL }}
+
+  "Notify":
+    runs-on: [ubuntu-docker-latest, linux/amd64]
+    needs: ["Build Image", "Validate Image"]
+    steps:
+      - name: Notify of success
+        uses: rjstone/discord-webhook-notify@v1
+        if: success()
+        with:
+          severity: info
+          details: Build succeeded!
+          webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
+          username: Gitea
+          avatarUrl: ${{ vars.RUNNER_ICON_URL }}
\ No newline at end of file
diff --git a/.gitea/workflows/build-tagged-release.yaml b/.gitea/workflows/build-tagged-release.yaml
new file mode 100644
index 0000000..219669e
--- /dev/null
+++ b/.gitea/workflows/build-tagged-release.yaml
@@ -0,0 +1,80 @@
+name: Build Tagged Release Image
+on:
+  push:
+    tags:
+      - '*'
+
+env:
+  FULL_TAG: git.mrmeeb.stream/mrmeeb/certbot-cron
+
+jobs:
+  "Build Image":
+    runs-on: [ubuntu-docker-latest, linux/amd64]
+    steps:
+      - name: Set up QEMU
+        uses: docker/setup-qemu-action@v3
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v3
+      - name: Login to Gitea Container Registry
+        uses: docker/login-action@v3
+        with:
+          registry: git.mrmeeb.stream
+          username: ${{ env.GITHUB_ACTOR }}
+          password: ${{ secrets.GTCR_TOKEN }}
+      - name: Docker metadata
+        id: meta
+        uses: docker/metadata-action@v5
+        with:
+          images: ${{ env.FULL_TAG }}
+          tags: |
+            type=pep440,pattern={{version}}            
+      - name: Build and push
+        uses: docker/build-push-action@v5
+        with:
+          push: true
+          platforms: linux/amd64,linux/arm64
+          tags: ${{ steps.meta.outputs.tags }}
+          provenance: false
+      - name: Notify on failure
+        uses: rjstone/discord-webhook-notify@v1
+        if: failure()
+        with:
+          severity: error
+          details: Build failed!
+          webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
+          username: Gitea
+          avatarUrl: ${{ vars.RUNNER_ICON_URL }}
+  "Create Release":
+    runs-on: ubuntu-latest
+    needs: ["Build Image"]
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+      - name: Set up Go
+        uses: actions/setup-go@v4
+        with: 
+          go-version: 1.22.2
+      - name: Run GoReleaser
+        uses: goreleaser/goreleaser-action@v5
+        with:
+          distribution: goreleaser
+          version: latest
+          args: release --clean -f .goreleaser-gitea.yaml
+        env:
+          GITEA_TOKEN: ${{ secrets.GORELEASER_TOKEN }}
+
+  "Notify":
+    runs-on: [ubuntu-docker-latest, linux/amd64]
+    needs: ["Build Image", "Create Release"]
+    steps:
+      - name: Notify of success
+        uses: rjstone/discord-webhook-notify@v1
+        if: success()
+        with:
+          severity: info
+          details: Build succeeded!
+          webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
+          username: Gitea
+          avatarUrl: ${{ vars.RUNNER_ICON_URL }}
\ No newline at end of file
diff --git a/.gitea/workflows/test-pr.yaml b/.gitea/workflows/test-pr.yaml
new file mode 100644
index 0000000..e4597f8
--- /dev/null
+++ b/.gitea/workflows/test-pr.yaml
@@ -0,0 +1,47 @@
+name: Test Pull Request
+on:
+  pull_request:
+    branches:
+      - 'main'
+      - 'develop'
+
+env:
+  FULL_TAG: git.mrmeeb.stream/mrmeeb/certbot-cron:develop
+
+jobs:
+  "Build Image":
+    runs-on: [ubuntu-docker-latest, linux/amd64]
+    steps:
+      - name: Set up QEMU
+        uses: docker/setup-qemu-action@v3
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v3
+      - name: Build
+        uses: docker/build-push-action@v5
+        with:
+          push: false
+          platforms: linux/amd64,linux/arm64
+          tags: ${{ env.FULL_TAG }}
+          provenance: false
+      - name: Notify on failure
+        uses: rjstone/discord-webhook-notify@v1
+        if: failure()
+        with:
+          severity: error
+          details: Build failed!
+          webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
+          username: Gitea
+          avatarUrl: ${{ vars.RUNNER_ICON_URL }}
+  "Notify":
+    runs-on: [ubuntu-docker-latest, linux/amd64]
+    needs: ["Build Image"]
+    steps:
+      - name: Notify of success
+        uses: rjstone/discord-webhook-notify@v1
+        if: success()
+        with:
+          severity: info
+          details: Build succeeded!
+          webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
+          username: Gitea
+          avatarUrl: ${{ vars.RUNNER_ICON_URL }}
\ No newline at end of file