diff --git a/.ci/ovn-kubernetes/Dockerfile b/.ci/ovn-kubernetes/Dockerfile
new file mode 100644
index 0000000000..ba04fb1990
--- /dev/null
+++ b/.ci/ovn-kubernetes/Dockerfile
@@ -0,0 +1,81 @@
+ARG OVNKUBE_COMMIT=master
+
+FROM fedora:33 AS ovnbuilder
+
+USER root
+
+ENV PYTHONDONTWRITEBYTECODE yes
+
+# install needed rpms - openvswitch must be 2.10.4 or higher
+RUN INSTALL_PKGS=" \
+    python3-pyyaml bind-utils procps-ng openssl numactl-libs firewalld-filesystem \
+    libpcap hostname \
+    python3-openvswitch python3-pyOpenSSL \
+    autoconf automake libtool g++ gcc fedora-packager rpmdevtools \
+    unbound unbound-devel groff python3-sphinx graphviz openssl openssl-devel \
+    checkpolicy libcap-ng-devel selinux-policy-devel" && \
+    dnf install --best --refresh -y --setopt=tsflags=nodocs $INSTALL_PKGS && \
+    dnf clean all && rm -rf /var/cache/dnf/*
+
+# Build OVS and OVN rpms from current folder
+RUN mkdir /tmp/ovn
+COPY . /tmp/ovn
+WORKDIR /tmp/ovn/ovs
+
+RUN ./boot.sh
+RUN ./configure -v
+RUN make && make rpm-fedora
+RUN rm rpm/rpmbuild/RPMS/x86_64/*debug*
+RUN rm rpm/rpmbuild/RPMS/x86_64/*devel*
+
+WORKDIR /tmp/ovn
+RUN ./boot.sh
+RUN ./configure
+RUN make && make rpm-fedora
+RUN rm rpm/rpmbuild/RPMS/x86_64/*debug*
+RUN rm rpm/rpmbuild/RPMS/x86_64/*docker*
+
+# Build ovn-kubernetes
+FROM golang:1.16 as ovnkubebuilder
+ARG OVNKUBE_COMMIT
+# Clone OVN Kubernetes and build the binary based on the commit passed as argument
+WORKDIR /root
+RUN git clone https://github.com/ovn-org/ovn-kubernetes.git
+WORKDIR /root/ovn-kubernetes/go-controller
+RUN git checkout ${OVNKUBE_COMMIT} && make
+
+# Build the final image
+FROM fedora:33
+
+# install needed dependencies
+RUN INSTALL_PKGS=" \
+    iptables iproute iputils hostname unbound-libs kubernetes-client kmod" && \
+    dnf install --best --refresh -y --setopt=tsflags=nodocs $INSTALL_PKGS && \
+    dnf clean all && rm -rf /var/cache/dnf/*
+
+RUN mkdir -p /var/run/openvswitch
+
+# install openvswitch and ovn rpms built in previous stages
+COPY --from=ovnbuilder /tmp/ovn/rpm/rpmbuild/RPMS/x86_64/*rpm ./
+COPY --from=ovnbuilder /tmp/ovn/ovs/rpm/rpmbuild/RPMS/x86_64/*rpm ./
+RUN dnf install -y *.rpm && rm -f *.rpm
+
+# install ovn-kubernetes binaries built in previous stage
+RUN mkdir -p /usr/libexec/cni/
+COPY --from=ovnkubebuilder /root/ovn-kubernetes/go-controller/_output/go/bin/ovnkube /usr/bin/
+COPY --from=ovnkubebuilder /root/ovn-kubernetes/go-controller/_output/go/bin/ovn-kube-util /usr/bin/
+COPY --from=ovnkubebuilder /root/ovn-kubernetes/go-controller/_output/go/bin/ovndbchecker /usr/bin/
+COPY --from=ovnkubebuilder /root/ovn-kubernetes/go-controller/_output/go/bin/ovn-k8s-cni-overlay /usr/libexec/cni/ovn-k8s-cni-overlay
+
+# ovnkube.sh is the entry point. This script examines environment
+# variables to direct operation and configure ovn
+COPY --from=ovnkubebuilder /root/ovn-kubernetes/dist/images/ovnkube.sh /root/
+COPY --from=ovnkubebuilder /root/ovn-kubernetes/dist/images/ovndb-raft-functions.sh /root/
+COPY --from=ovnkubebuilder /root/ovn-kubernetes/dist/images/iptables-scripts /usr/sbin/
+
+LABEL io.k8s.display-name="ovn-kubernetes" \
+    io.k8s.description="This is a Kubernetes network plugin that provides an overlay network using OVN." \
+    maintainer="ovn team"
+
+WORKDIR /root
+ENTRYPOINT /root/ovnkube.sh
diff --git a/.github/workflows/ovn-kubernetes.yml b/.github/workflows/ovn-kubernetes.yml
new file mode 100644
index 0000000000..ee77613f65
--- /dev/null
+++ b/.github/workflows/ovn-kubernetes.yml
@@ -0,0 +1,170 @@
+name: ovn-kubernetes
+
+on:
+  push:
+  pull_request:
+  workflow_dispatch:
+  schedule:
+  # Run Sunday at midnight
+  - cron: '0 0 * * 0'
+
+env:
+  GO_VERSION: "1.16.3"
+  K8S_VERSION: v1.20.2
+  OVNKUBE_COMMIT: "f669ef6d35c86bf46d7a1055072e48ea1506f88c"
+  KIND_CLUSTER_NAME: ovn
+  KIND_INSTALL_INGRESS: true
+  KIND_ALLOW_SYSTEM_WRITES: true
+  # This skips tests tagged as Serial
+  # Current Serial tests are not relevant for OVN
+  PARALLEL: true
+
+jobs:
+  build:
+    name: Build
+    runs-on: ubuntu-20.04
+    steps:
+    - name: Check out ovn
+      uses: actions/checkout@v2
+      with:
+        submodules: recursive
+
+    - name: Build ovn-kubernetes container
+      run: |
+        docker build --build-arg OVNKUBE_COMMIT=${{ env.OVNKUBE_COMMIT }} -t ovn-daemonset-f:dev -f .ci/ovn-kubernetes/Dockerfile .
+        mkdir /tmp/_output
+        docker save ovn-daemonset-f:dev > /tmp/_output/image.tar
+
+    - uses: actions/upload-artifact@v2
+      with:
+        name: test-image
+        path: /tmp/_output/image.tar
+
+  e2e:
+    name: e2e
+    if: github.event_name != 'schedule'
+    runs-on: ubuntu-20.04
+    timeout-minutes: 120
+    strategy:
+      fail-fast: false
+      matrix:
+        target:
+          - shard: shard-conformance
+            hybrid-overlay: false
+            multicast-enable: false
+            emptylb-enable: false
+          - shard: control-plane
+            hybrid-overlay: true
+            multicast-enable: true
+            emptylb-enable: true
+        ipfamily:
+          - ip: ipv4
+            name: "IPv4"
+            ipv4: true
+            ipv6: false
+          - ip: ipv6
+            name: "IPv6"
+            ipv4: false
+            ipv6: true
+          - ip: dualstack
+            name: "Dualstack"
+            ipv4: true
+            ipv6: true
+        # Example of how to exclude a fully qualified test:
+        # - {"ipfamily": {"ip": ipv4}, "ha": {"enabled": "false"}, "gateway-mode": shared, "target": {"shard": shard-n-other}}
+        exclude:
+         # Not currently supported but needs to be.
+         - {"ipfamily": {"ip": dualstack}, "target": {"shard": control-plane}}
+         - {"ipfamily": {"ip": ipv6}, "target": {"shard": control-plane}}
+    needs: [build]
+    env:
+      JOB_NAME: "${{ matrix.target.shard }}-${{ matrix.ipfamily.name }}"
+      OVN_HA: "true"
+      KIND_IPV4_SUPPORT: "${{ matrix.ipfamily.ipv4 }}"
+      KIND_IPV6_SUPPORT: "${{ matrix.ipfamily.ipv6 }}"
+      OVN_HYBRID_OVERLAY_ENABLE: "${{ matrix.target.hybrid-overlay }}"
+      OVN_GATEWAY_MODE: "shared"
+      OVN_MULTICAST_ENABLE:  "${{ matrix.target.multicast-enable }}"
+      OVN_EMPTY_LB_EVENTS: "${{ matrix.target.emptylb-enable }}"
+    steps:
+
+    - name: Free up disk space
+      run: sudo eatmydata apt-get remove --auto-remove -y aspnetcore-* dotnet-* libmono-* mono-* msbuild php-* php7* ghc-* zulu-*
+
+    - name: Set up Go
+      uses: actions/setup-go@v2
+      with:
+        go-version: ${{ env.GO_VERSION }}
+      id: go
+
+    - name: Check out ovn-kubernetes
+      uses: actions/checkout@v2
+      with:
+          path: src/github.com/ovn-org/ovn-kubernetes
+          repository: ovn-org/ovn-kubernetes
+
+    - name: Set up environment
+      run: |
+        export GOPATH=$(go env GOPATH)
+        echo "GOPATH=$GOPATH" >> $GITHUB_ENV
+        echo "$GOPATH/bin" >> $GITHUB_PATH
+
+    - name: Disable ufw
+      # For IPv6 and Dualstack, ufw (Uncomplicated Firewall) should be disabled.
+      # Not needed for KIND deployments, so just disable all the time.
+      run: |
+        sudo ufw disable
+
+    - uses: actions/download-artifact@v2
+      with:
+        name: test-image
+
+    - name: Load docker image
+      run: |
+        docker load --input image.tar
+
+    - name: kind setup
+      run: |
+        export OVN_IMAGE="ovn-daemonset-f:dev"
+        make -C test install-kind
+      working-directory: src/github.com/ovn-org/ovn-kubernetes
+
+    - name: Run Tests
+      run: |
+        make -C test ${{ matrix.target.shard }}
+      working-directory: src/github.com/ovn-org/ovn-kubernetes
+
+    - name: Upload Junit Reports
+      if: always()
+      uses: actions/upload-artifact@v2
+      with:
+        name: kind-junit-${{ env.JOB_NAME }}-${{ github.run_id }}
+        path: 'src/github.com/ovn-org/ovn-kubernetes/test/_artifacts/*.xml'
+
+    - name: Generate Test Report
+      id: xunit-viewer
+      if: always()
+      uses: AutoModality/action-xunit-viewer@v1
+      with:
+        results: src/github.com/ovn-org/ovn-kubernetes/test/_artifacts/
+
+    - name: Upload Test Report
+      if: always()
+      uses: actions/upload-artifact@v2
+      with:
+        name: test-report-${{ env.JOB_NAME }}-${{ github.run_id }}
+        path: 'src/github.com/ovn-org/ovn-kubernetes/test/_artifacts/index.html'
+
+    - name: Export logs
+      if: always()
+      run: |
+        mkdir -p /tmp/kind/logs
+        kind export logs --name ${KIND_CLUSTER_NAME} --loglevel=debug /tmp/kind/logs
+      working-directory: src/github.com/ovn-org/ovn-kubernetes
+
+    - name: Upload logs
+      if: always()
+      uses: actions/upload-artifact@v2
+      with:
+        name: kind-logs-${{ env.JOB_NAME }}-${{ github.run_id }}
+        path: /tmp/kind/logs
diff --git a/Makefile.am b/Makefile.am
index 80247b62d5..0169c96ef8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -90,7 +90,9 @@ EXTRA_DIST = \
 	.ci/linux-prepare.sh \
 	.ci/osx-build.sh \
 	.ci/osx-prepare.sh \
+	.ci/ovn-kubernetes/Dockerfile \
 	.github/workflows/test.yml \
+	.github/workflows/ovn-kubernetes.yml \
 	boot.sh \
 	$(MAN_FRAGMENTS) \
 	$(MAN_ROOTS) \
