diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml
deleted file mode 100644
index 7d46695..0000000
--- a/.github/workflows/api.yml
+++ /dev/null
@@ -1,42 +0,0 @@
-name: API
-
-on:
-  push:
-    branches: [ master ]
-  pull_request:
-    branches: [ master ]
-
-jobs:
-
-  build:
-    name: Build
-    runs-on: ubuntu-latest
-    
-    defaults:
-      run:
-        working-directory: api
-    
-    steps:
-
-    - name: Set up Go 1.x
-      uses: actions/setup-go@v2
-      with:
-        go-version: ^1.13
-      id: go
-
-    - name: Check out code into the Go module directory
-      uses: actions/checkout@v2
-
-    - name: Get dependencies
-      run: |
-        go get -v -t -d ./...
-        if [ -f Gopkg.toml ]; then
-            curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
-            dep ensure
-        fi
-
-    - name: Build
-      run: go build -v .
-
-    - name: Test
-      run: go test -v ./...
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..1c601cd
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,69 @@
+name: build
+
+on:
+  schedule:
+    - cron: "0 10 * * *" # everyday at 10am
+  pull_request:
+    branches: master
+  push:
+    branches: master
+    tags:
+      - v*
+
+jobs:
+  buildx:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+      - name: Prepare
+        id: prepare
+        run: |
+          DOCKER_USERNAME=viktorstrate
+          DOCKER_IMAGE=viktorstrate/photoview
+          DOCKER_PLATFORMS=linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/386,linux/ppc64le,linux/s390x
+          VERSION=edge
+
+          if [[ $GITHUB_REF == refs/tags/* ]]; then
+            VERSION=${GITHUB_REF#refs/tags/v}
+          fi
+          if [ "${{ github.event_name }}" = "schedule" ]; then
+            VERSION=nightly
+          fi
+
+          TAGS="--tag ${DOCKER_IMAGE}:${VERSION}"
+          if [[ $VERSION =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
+            TAGS="$TAGS --tag ${DOCKER_IMAGE}:latest"
+          fi
+
+          echo ::set-output name=docker_username::${DOCKER_USERNAME}
+          echo ::set-output name=docker_image::${DOCKER_IMAGE}
+          echo ::set-output name=version::${VERSION}
+          echo ::set-output name=buildx_args::--platform ${DOCKER_PLATFORMS} \
+            --build-arg VERSION=${VERSION} \
+            --build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
+            --build-arg VCS_REF=${GITHUB_SHA::8} \
+            ${TAGS} --file Dockerfile .
+      - name: Set up Docker Buildx
+        uses: crazy-max/ghaction-docker-buildx@v3
+      - name: Docker Buildx (build)
+        run: |
+          docker buildx build --output "type=image,push=false" ${{ steps.prepare.outputs.buildx_args }}
+      - name: Docker Login
+        if: success() && github.event_name != 'pull_request'
+        env:
+          DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
+        run: |
+          echo "${DOCKER_PASSWORD}" | docker login --username "${{ steps.prepare.outputs.docker_username }}" --password-stdin
+      - name: Docker Buildx (push)
+        if: success() && github.event_name != 'pull_request'
+        run: |
+          docker buildx build --output "type=image,push=true" ${{ steps.prepare.outputs.buildx_args }}
+      - name: Docker Check Manifest
+        if: always() && github.event_name != 'pull_request'
+        run: |
+          docker run --rm mplatform/mquery ${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.version }}
+      - name: Clear
+        if: always() && github.event_name != 'pull_request'
+        run: |
+          rm -f ${HOME}/.docker/config.json
diff --git a/.github/workflows/tests-api.yml b/.github/workflows/tests-api.yml
new file mode 100644
index 0000000..90d7675
--- /dev/null
+++ b/.github/workflows/tests-api.yml
@@ -0,0 +1,40 @@
+name: API Tests
+
+on:
+  push:
+    branches: [master]
+  pull_request:
+    branches: [master]
+
+jobs:
+  test-api:
+    name: Test API
+    runs-on: ubuntu-latest
+
+    defaults:
+      run:
+        working-directory: api
+
+    steps:
+      - name: Set up Go 1.x
+        uses: actions/setup-go@v2
+        with:
+          go-version: ^1.13
+        id: go
+
+      - name: Check out code into the Go module directory
+        uses: actions/checkout@v2
+
+      - name: Get dependencies
+        run: |
+          go get -v -t -d ./...
+          if [ -f Gopkg.toml ]; then
+              curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
+              dep ensure
+          fi
+
+      - name: Build
+        run: go build -v .
+
+      - name: Test
+        run: go test -v ./...
diff --git a/Dockerfile b/Dockerfile
index 3e28385..3c8e05f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
 # Build UI
-FROM node:10 as ui
+FROM --platform=${BUILDPLATFORM:-linux/amd64} node:10 as ui
 
 ARG API_ENDPOINT
 ENV API_ENDPOINT=${API_ENDPOINT}
@@ -20,7 +20,12 @@ COPY ui /app
 RUN npm run build -- --public-url $UI_PUBLIC_URL
 
 # Build API
-FROM golang:alpine AS api
+FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.14-alpine AS api
+
+# Convert TARGETPLATFORM to GOARCH format
+# https://github.com/tonistiigi/xx
+COPY --from=tonistiigi/xx:golang / /
+ARG TARGETPLATFORM
 
 RUN mkdir -p /app
 WORKDIR /app
@@ -32,7 +37,7 @@ RUN go mod download
 # Copy api source
 COPY api /app
 
-RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o photoview .
+RUN go env && go build -v -o photoview .
 
 # Copy api and ui to production environment
 FROM alpine:3.12
