diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..c200f675
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,40 @@
+.DS_Store
+/.git
+/.github
+/.vscode
+.idea
+/AdGuardHome
+/AdGuardHome.exe
+/AdGuardHome.yaml
+/AdGuardHome.log
+/data
+/build
+/dist
+/client/node_modules
+/.gitattributes
+/.gitignore
+/.goreleaser.yml
+/changelog.config.js
+/coverage.txt
+/Dockerfile
+/LICENSE.txt
+/Makefile
+/querylog.json
+/querylog.json.1
+/*.md
+
+# Test output
+dnsfilter/tests/top-1m.csv
+dnsfilter/tests/dnsfilter.TestLotsOfRules*.pprof
+
+# Snapcraft build temporary files
+*.snap
+launchpad_credentials
+snapcraft_login
+snapcraft.yaml.bak
+
+# IntelliJ IDEA project files
+*.iml
+
+# Packr
+*-packr.go
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 00000000..5c75c8ba
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,172 @@
+name: build
+
+env:
+  GO_VERSION: 1.14
+  NODE_VERSION: 13
+
+on:
+  push:
+    branches:
+      - '*'
+    tags:
+      - v*
+  pull_request:
+
+jobs:
+
+  test:
+    runs-on: ${{ matrix.os }}
+    env:
+      GO111MODULE: on
+      GOPROXY: https://goproxy.io
+    strategy:
+      fail-fast: false
+      matrix:
+        os:
+          - ubuntu-latest
+          - macOS-latest
+          - windows-latest
+    steps:
+      -
+        name: Checkout
+        uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+      -
+        name: Set up Go
+        uses: actions/setup-go@v2
+        with:
+          go-version: ${{ env.GO_VERSION }}
+
+      -
+        name: Set up Node
+        uses: actions/setup-node@v1
+        with:
+          node-version: ${{ env.NODE_VERSION }}
+      -
+        name: Set up Go modules cache
+        uses: actions/cache@v2
+        with:
+          path: ~/go/pkg/mod
+          key: ${{ runner.os }}-go-${{ hashFiles('go.sum') }}
+          restore-keys: |
+            ${{ runner.os }}-go-
+      -
+        name: Get npm cache directory
+        id: npm-cache
+        run: |
+          echo "::set-output name=dir::$(npm config get cache)"
+      -
+        name: Set up npm cache
+        uses: actions/cache@v2
+        with:
+          path: ${{ steps.npm-cache.outputs.dir }}
+          key: ${{ runner.os }}-node-${{ hashFiles('client/package-lock.json') }}
+          restore-keys: |
+            ${{ runner.os }}-node-
+      -
+        name: Run make ci
+        shell: bash
+        run: |
+          make ci
+      -
+        name: Upload coverage
+        uses: codecov/codecov-action@v1
+        if: success() && matrix.os == 'ubuntu-latest'
+        with:
+          token: ${{ secrets.CODECOV_TOKEN }}
+          file: ./coverage.txt
+
+  app:
+    runs-on: ubuntu-latest
+    needs: test
+    steps:
+      -
+        name: Checkout
+        uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+      -
+        name: Set up Go
+        uses: actions/setup-go@v2
+        with:
+          go-version: ${{ env.GO_VERSION }}
+      -
+        name: Set up Node
+        uses: actions/setup-node@v1
+        with:
+          node-version: ${{ env.NODE_VERSION }}
+      -
+        name: Set up Go modules cache
+        uses: actions/cache@v2
+        with:
+          path: ~/go/pkg/mod
+          key: ${{ runner.os }}-go-${{ hashFiles('go.sum') }}
+          restore-keys: |
+            ${{ runner.os }}-go-
+      -
+        name: Get npm cache directory
+        id: npm-cache
+        run: |
+          echo "::set-output name=dir::$(npm config get cache)"
+      -
+        name: Set up node_modules cache
+        uses: actions/cache@v2
+        with:
+          path: ${{ steps.npm-cache.outputs.dir }}
+          key: ${{ runner.os }}-node-${{ hashFiles('client/package-lock.json') }}
+          restore-keys: |
+            ${{ runner.os }}-node-
+      -
+        name: Set up Snapcraft
+        run: |
+          sudo apt-get -yq --no-install-suggests --no-install-recommends install snapcraft
+      -
+        name: Set up GoReleaser
+        run: |
+          curl -sfL https://install.goreleaser.com/github.com/goreleaser/goreleaser.sh | BINDIR="$(go env GOPATH)/bin" sh
+      -
+        name: Run snapshot build
+        run: |
+          make release
+
+  docker:
+    runs-on: ubuntu-latest
+    needs: test
+    steps:
+      -
+        name: Set up Docker Buildx
+        uses: crazy-max/ghaction-docker-buildx@v1
+      -
+        name: Checkout
+        uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+      -
+        name: Docker Buildx (build)
+        run: |
+          make docker-multi-arch
+      -
+        name: Clear
+        if: always() && startsWith(github.ref, 'refs/tags/v')
+        run: |
+          rm -f ${HOME}/.docker/config.json
+
+  notify:
+    needs: [app, docker]
+    # Secrets are not passed to workflows that are triggered by a pull request from a fork
+    if: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository }}
+    runs-on: ubuntu-latest
+    steps:
+      -
+        name: Conclusion
+        uses: technote-space/workflow-conclusion-action@v1
+      -
+        name: Send Slack notif
+        uses: 8398a7/action-slack@v3
+        with:
+          status: ${{ env.WORKFLOW_CONCLUSION }}
+          fields: repo,message,commit,author
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 00000000..99fcefc4
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,47 @@
+name: golangci-lint
+on:
+  push:
+    tags:
+      - v*
+    branches:
+      - '*'
+  pull_request:
+jobs:
+  golangci:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - name: golangci-lint
+        uses: golangci/golangci-lint-action@v1
+        with:
+          # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
+          version: v1.27
+
+  eslint:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - name: Install modules
+        run: npm --prefix client ci
+      - name: Run ESLint
+        run: npm --prefix client run lint
+
+
+  notify:
+    needs: [golangci,eslint]
+    # Secrets are not passed to workflows that are triggered by a pull request from a fork
+    if: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository }}
+    runs-on: ubuntu-latest
+    steps:
+      -
+        name: Conclusion
+        uses: technote-space/workflow-conclusion-action@v1
+      -
+        name: Send Slack notif
+        uses: 8398a7/action-slack@v3
+        with:
+          status: ${{ env.WORKFLOW_CONCLUSION }}
+          fields: repo,message,commit,author
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index d47eef0c..e7f55736 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,7 +11,6 @@
 /client/node_modules/
 /querylog.json
 /querylog.json.1
-/a_main-packr.go
 coverage.txt
 
 # Test output
@@ -26,3 +25,6 @@ snapcraft.yaml.bak
 
 # IntelliJ IDEA project files
 *.iml
+
+# Packr
+*-packr.go
diff --git a/.golangci.yml b/.golangci.yml
index 47425e3f..be43ec74 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -15,6 +15,9 @@ run:
     - dnsfilter/rule_to_regexp.go
     - util/pprof.go
     - ".*_test.go"
+    - client/.*
+    - build/.*
+    - dist/.*
 
 
 # all available settings of specific linters
diff --git a/.gometalinter.json b/.gometalinter.json
deleted file mode 100644
index 10de7f42..00000000
--- a/.gometalinter.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
-  "Vendor": true,
-  "Test": true,
-  "Deadline": "2m",
-  "Sort": ["linter", "severity", "path", "line"],
-  "Exclude": [
-    ".*generated.*",
-    "dnsfilter/rule_to_regexp.go"
-  ],
-  "EnableGC": true,
-  "Linters": {
-    "nakedret": {
-      "Command": "nakedret",
-      "Pattern": "^(?P<path>.*?\\.go):(?P<line>\\d+)\\s*(?P<message>.*)$"
-    }
-  },
-  "WarnUnmatchedDirective": true,
-
-  "EnableAll": true,
-  "DisableAll": false,
-  "Disable": [
-    "maligned",
-    "goconst",
-    "vetshadow"
-  ],
-
-  "Cyclo": 20,
-  "LineLength": 200
-}
\ No newline at end of file
diff --git a/.goreleaser.yml b/.goreleaser.yml
new file mode 100644
index 00000000..b2e11b09
--- /dev/null
+++ b/.goreleaser.yml
@@ -0,0 +1,90 @@
+project_name: AdGuardHome
+
+env:
+  - GO111MODULE=on
+  - GOPROXY=https://goproxy.io
+
+before:
+  hooks:
+    - go mod download
+    - go generate ./...
+
+builds:
+  -
+    main: ./main.go
+    ldflags:
+      - -s -w -X main.version={{.Version}} -X main.channel={{.Env.CHANNEL}} -X main.goarm={{.Env.GOARM}}
+    env:
+      - CGO_ENABLED=0
+    goos:
+      - darwin
+      - linux
+      - freebsd
+      - windows
+    goarch:
+      - 386
+      - amd64
+      - arm
+      - arm64
+      - mips
+      - mipsle
+      - mips64
+      - mips64le
+    goarm:
+      - 5
+      - 6
+      - 7
+    gomips:
+      - softfloat
+    ignore:
+      - goos: freebsd
+        goarch: mips
+      - goos: freebsd
+        goarch: mipsle
+
+archives:
+  -
+    # Archive name template.
+    # Defaults:
+    # - if format is `tar.gz`, `tar.xz`, `gz` or `zip`:
+    #   - `{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}`
+    # - if format is `binary`:
+    #   - `{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}`
+    name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}"
+    wrap_in_directory: "AdGuardHome"
+    format_overrides:
+      - goos: windows
+        format: zip
+      - goos: darwin
+        format: zip
+    files:
+      - LICENSE.txt
+      - README.md
+
+snapcrafts:
+  -
+    name: adguard-home
+    base: core18
+    name_template: '{{ .ProjectName }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
+    summary: Network-wide ads & trackers blocking DNS server
+    description: |
+      AdGuard Home is a network-wide software for blocking ads & tracking. After
+      you set it up, it'll cover ALL your home devices, and you don't need any
+      client-side software for that.
+
+      It operates as a DNS server that re-routes tracking domains to a "black hole,"
+      thus preventing your devices from connecting to those servers. It's based
+      on software we use for our public AdGuard DNS servers -- both share a lot
+      of common code.
+    grade: stable
+    confinement: strict
+    publish: false
+    license: GPL-3.0
+    apps:
+      adguard-home:
+        command: AdGuardHome -w $SNAP_DATA --no-check-update
+        plugs: [ network-bind ]
+        daemon: simple
+
+checksum:
+  name_template: 'checksums.txt'
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index b2e5a2dd..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,121 +0,0 @@
-if: repo = AdguardTeam/AdGuardHome
-language: go
-sudo: false
-
-go:
-  - 1.14.x
-os:
-  - linux
-  - osx
-  - windows
-
-before_install:
-  - |-
-    case $TRAVIS_OS_NAME in
-      linux | osx)
-        nvm install node
-        npm install -g npm
-        curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(go env GOPATH)/bin v1.23.8
-        ;;
-      windows)
-        # Using NVS for managing Node.js versions on Windows
-        NVS_HOME="C:\ProgramData\nvs"
-        git clone --single-branch https://github.com/jasongin/nvs $NVS_HOME
-        source $NVS_HOME/nvs.sh
-        nvs add latest
-        nvs use latest
-        ;;
-    esac
-
-install:
-  - |-
-    case $TRAVIS_OS_NAME in
-      linux | osx)
-        node --version
-        npm --version
-        npm --prefix client ci
-        ;;
-      windows)
-        node --version
-        npm --version
-        nvs --version
-        npm --prefix client ci
-        ;;
-    esac
-
-cache:
-  directories:
-    - $HOME/.cache/go-build
-    - $HOME/gopath/pkg/mod
-    - $HOME/Library/Caches/go-build
-
-script:
-  - |-
-    case $TRAVIS_OS_NAME in
-      linux | osx)
-        /bin/bash ci.sh
-        ;;
-      windows)
-        npm --prefix client run build-prod
-        go test -race -v -bench=. -coverprofile=coverage.txt -covermode=atomic ./...
-        ;;
-    esac
-
-after_success:
-  - |-
-    case $TRAVIS_OS_NAME in
-      linux)
-        bash <(curl -s https://codecov.io/bash)
-        ;;
-    esac
-
-notifications:
-  slack: performix:yXTihlSzsLFSZiqbXMNzvTSX
-
-matrix:
-  include:
-    # Release build configuration
-    - if: repo = AdguardTeam/AdGuardHome
-    - name: release
-      go:
-        - 1.14.x
-      os:
-        - linux
-
-      script:
-        - node -v
-        - npm -v
-        # Prepare releases
-        - ./build_release.sh
-        - ls -l dist
-
-      deploy:
-        provider: releases
-        api_key: $GITHUB_TOKEN
-        file:
-          - dist/AdGuardHome_*
-        on:
-          repo: AdguardTeam/AdGuardHome
-          tags: true
-        draft: true
-        file_glob: true
-        skip_cleanup: true
-
-    # Docker build configuration
-    - if: repo = AdguardTeam/AdGuardHome
-    - name: docker
-      if: type != pull_request AND (branch = master OR tag IS present) AND repo = AdguardTeam/AdGuardHome
-      go:
-        - 1.14.x
-      os:
-        - linux
-      services:
-        - docker
-      before_script:
-        - nvm install node
-        - npm install -g npm
-      script:
-        - docker login -u="$DOCKER_USER" -p="$DOCKER_PASSWORD"
-        - ./build_docker.sh
-      after_script:
-        - docker images
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..49ff6118
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,78 @@
+FROM --platform=${BUILDPLATFORM:-linux/amd64} tonistiigi/xx:golang AS xgo
+FROM --platform=${BUILDPLATFORM:-linux/amd64} golang:1.14-alpine as builder
+
+ARG BUILD_DATE
+ARG VCS_REF
+ARG VERSION=dev
+ARG CHANNEL=release
+
+ENV CGO_ENABLED 0
+ENV GO111MODULE on
+ENV GOPROXY https://goproxy.io
+
+COPY --from=xgo / /
+RUN go env
+
+RUN apk --update --no-cache add \
+    build-base \
+    gcc \
+    git \
+    npm \
+  && rm -rf /tmp/* /var/cache/apk/*
+
+WORKDIR /app
+
+COPY . ./
+
+# Prepare the client code
+RUN npm --prefix client ci && npm --prefix client run build-prod
+
+# Download go dependencies
+RUN go mod download
+RUN go generate ./...
+
+# It's important to place TARGET* arguments here to avoid running npm and go mod download for every platform
+ARG TARGETPLATFORM
+ARG TARGETOS
+ARG TARGETARCH
+RUN go build -ldflags="-s -w -X main.version=${VERSION} -X main.channel=${CHANNEL} -X main.goarm=${GOARM}"
+
+FROM --platform=${TARGETPLATFORM:-linux/amd64} alpine:latest
+
+ARG BUILD_DATE
+ARG VCS_REF
+ARG VERSION
+ARG CHANNEL
+
+LABEL maintainer="AdGuard Team <devteam@adguard.com>" \
+  org.opencontainers.image.created=$BUILD_DATE \
+  org.opencontainers.image.url="https://adguard.com/adguard-home.html" \
+  org.opencontainers.image.source="https://github.com/AdguardTeam/AdGuardHome" \
+  org.opencontainers.image.version=$VERSION \
+  org.opencontainers.image.revision=$VCS_REF \
+  org.opencontainers.image.vendor="AdGuard" \
+  org.opencontainers.image.title="AdGuard Home" \
+  org.opencontainers.image.description="Network-wide ads & trackers blocking DNS server" \
+  org.opencontainers.image.licenses="GPL-3.0"
+
+RUN apk --update --no-cache add \
+    ca-certificates \
+    libcap \
+    libressl \
+    tzdata \
+  && rm -rf /tmp/* /var/cache/apk/*
+
+COPY --from=builder --chown=nobody:nogroup /app/AdGuardHome /opt/adguardhome/AdGuardHome
+COPY --from=builder --chown=nobody:nogroup /usr/local/go/lib/time/zoneinfo.zip /usr/local/go/lib/time/zoneinfo.zip
+
+RUN /opt/adguardhome/AdGuardHome --version \
+  && mkdir -p /opt/adguardhome/conf /opt/adguardhome/work \
+  && chown -R nobody: /opt/adguardhome \
+  && setcap 'cap_net_bind_service=+eip' /opt/adguardhome/AdGuardHome
+
+EXPOSE 53/tcp 53/udp 67/udp 68/udp 80/tcp 443/tcp 853/tcp 3000/tcp
+WORKDIR /opt/adguardhome/work
+VOLUME ["/opt/adguardhome/conf", "/opt/adguardhome/work"]
+
+ENTRYPOINT ["/opt/adguardhome/AdGuardHome"]
+CMD ["-h", "0.0.0.0", "-c", "/opt/adguardhome/conf/AdGuardHome.yaml", "-w", "/opt/adguardhome/work", "--no-check-update"]
diff --git a/Dockerfile.travis b/Dockerfile.travis
deleted file mode 100644
index 0e11574e..00000000
--- a/Dockerfile.travis
+++ /dev/null
@@ -1,23 +0,0 @@
-FROM alpine:latest
-LABEL maintainer="AdGuard Team <devteam@adguard.com>"
-
-# Update CA certs
-RUN apk --no-cache --update add ca-certificates libcap && \
-    rm -rf /var/cache/apk/* && \
-    mkdir -p /opt/adguardhome/conf /opt/adguardhome/work && \
-    chown -R nobody: /opt/adguardhome
-
-COPY --chown=nobody:nogroup ./AdGuardHome /opt/adguardhome/AdGuardHome
-
-RUN setcap 'cap_net_bind_service=+eip' /opt/adguardhome/AdGuardHome
-
-EXPOSE 53/tcp 53/udp 67/udp 68/udp 80/tcp 443/tcp 853/tcp 3000/tcp
-
-VOLUME ["/opt/adguardhome/conf", "/opt/adguardhome/work"]
-
-WORKDIR /opt/adguardhome/work
-
-#USER nobody
-
-ENTRYPOINT ["/opt/adguardhome/AdGuardHome"]
-CMD ["-h", "0.0.0.0", "-c", "/opt/adguardhome/conf/AdGuardHome.yaml", "-w", "/opt/adguardhome/work", "--no-check-update"]
diff --git a/Makefile b/Makefile
index 65cea96f..f577ea7c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,43 +1,226 @@
-GIT_VERSION := $(shell git describe --abbrev=4 --dirty --always --tags)
-NATIVE_GOOS = $(shell unset GOOS; go env GOOS)
-NATIVE_GOARCH = $(shell unset GOARCH; go env GOARCH)
+#
+# Available targets
+#
+# * build -- builds AdGuardHome for the current platform
+# * client -- builds client-side code of AdGuard Home
+# * client-watch -- builds client-side code of AdGuard Home and watches for changes there
+# * docker -- builds a docker image for the current platform
+# * clean -- clean everything created by previous builds
+# * lint -- run all linters
+# * test -- run all unit-tests
+# * dependencies -- installs dependencies (go and npm modules)
+# * ci -- installs dependencies, runs linters and tests, intended to be used by CI/CD
+#
+# Building releases:
+#
+# * release -- builds AdGuard Home distros. CHANNEL must be specified (edge, release or beta).
+# * docker-multi-arch -- builds a multi-arch image. If you want it to be pushed to docker hub,
+# 	you must specify:
+#     * DOCKER_IMAGE_NAME - adguard/adguard-home
+#     * DOCKER_OUTPUT - type=image,name=adguard/adguard-home,push=true
+
 GOPATH := $(shell go env GOPATH)
-JSFILES = $(shell find client -path client/node_modules -prune -o -type f -name '*.js')
-STATIC = build/static/index.html
-CHANNEL ?= release
-DOCKER_IMAGE_DEV_NAME=adguardhome-dev
-DOCKERFILE=packaging/docker/Dockerfile
-DOCKERFILE_HUB=packaging/docker/Dockerfile.travis
-
+PWD := $(shell pwd)
 TARGET=AdGuardHome
+BASE_URL="https://static.adguard.com/adguardhome/$(CHANNEL)"
 
-.PHONY: all build clean
+# See release target
+DIST_DIR=dist
+
+# Update channel. Can be release, beta or edge. Uses edge by default.
+CHANNEL ?= edge
+
+# Validate channel
+ifneq ($(CHANNEL),relese)
+ifneq ($(CHANNEL),beta)
+ifneq ($(CHANNEL),edge)
+$(error CHANNEL value is not valid. Valid values are release,beta or edge)
+endif
+endif
+endif
+
+# goreleaser command depends on the $CHANNEL
+GORELEASER_COMMAND=goreleaser release --rm-dist --skip-publish --snapshot
+ifneq ($(CHANNEL),edge)
+	# If this is not an "edge" build, use normal release command
+	GORELEASER_COMMAND=goreleaser release --rm-dist --skip-publish
+endif
+
+# Version properties
+COMMIT=$(shell git rev-parse --short HEAD)
+TAG_NAME=$(shell git describe --abbrev=0)
+RELEASE_VERSION=$(TAG_NAME)
+SNAPSHOT_VERSION=$(RELEASE_VERSION)-SNAPSHOT-$(COMMIT)
+
+# Set proper version
+VERSION=
+ifeq ($(TAG_NAME),$(shell git describe --abbrev=4))
+	VERSION=$(RELEASE_VERSION)
+else
+	VERSION=$(SNAPSHOT_VERSION)
+endif
+
+# Docker target parameters
+DOCKER_IMAGE_NAME ?= adguardhome-dev
+DOCKER_IMAGE_FULL_NAME = $(DOCKER_IMAGE_NAME):$(VERSION)
+DOCKER_PLATFORMS=linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/386,linux/ppc64le
+DOCKER_OUTPUT ?= type=image,name=$(DOCKER_IMAGE_NAME),push=false
+BUILD_DATE=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')
+
+# Docker tags (can be redefined)
+DOCKER_TAGS ?=
+ifndef DOCKER_TAGS
+	ifeq ($(CHANNEL),release)
+		DOCKER_TAGS := --tag $(DOCKER_IMAGE_NAME):latest
+	endif
+	ifeq ($(CHANNEL),beta)
+		DOCKER_TAGS := --tag $(DOCKER_IMAGE_NAME):beta
+	endif
+	ifeq ($(CHANNEL),edge)
+		# Don't set the version tag when pushing to "edge"
+		DOCKER_IMAGE_FULL_NAME := $(DOCKER_IMAGE_NAME):edge
+		# DOCKER_TAGS := --tag $(DOCKER_IMAGE_NAME):edge
+	endif
+endif
+
+# Validate docker build arguments
+ifndef DOCKER_IMAGE_NAME
+$(error DOCKER_IMAGE_NAME value is not set)
+endif
+
+.PHONY: all build client client-watch docker lint test dependencies clean release docker-multi-arch
 all: build
 
-build: $(TARGET)
-
-client/node_modules: client/package.json client/package-lock.json
-	npm --prefix client ci
-	touch client/node_modules
-
-$(STATIC): $(JSFILES) client/node_modules
-	npm --prefix client run build-prod
-
-$(TARGET): $(STATIC) *.go home/*.go dhcpd/*.go dnsfilter/*.go dnsforward/*.go
-	GOOS=$(NATIVE_GOOS) GOARCH=$(NATIVE_GOARCH) GO111MODULE=off go get -v github.com/gobuffalo/packr/...
-	PATH=$(GOPATH)/bin:$(PATH) packr -z
-	CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=$(GIT_VERSION) -X main.channel=$(CHANNEL) -X main.goarm=$(GOARM)" -asmflags="-trimpath=$(PWD)" -gcflags="-trimpath=$(PWD)"
+build: dependencies client
+	go generate ./...
+	CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=$(VERSION) -X main.channel=$(CHANNEL) -X main.goarm=$(GOARM)"
 	PATH=$(GOPATH)/bin:$(PATH) packr clean
 
+client:
+	npm --prefix client run build-prod
+
+client-watch:
+	npm --prefix client run watch
+
 docker:
-	docker build -t "$(DOCKER_IMAGE_DEV_NAME)" -f "$(DOCKERFILE)" .
+	DOCKER_CLI_EXPERIMENTAL=enabled \
+	docker buildx build \
+	--build-arg VERSION=$(VERSION) \
+	--build-arg CHANNEL=$(CHANNEL) \
+	--build-arg VCS_REF=$(COMMIT) \
+	--build-arg BUILD_DATE=$(BUILD_DATE) \
+	$(DOCKER_TAGS) \
+	--load \
+	-t "$(DOCKER_IMAGE_NAME)" -f ./Dockerfile .
+
 	@echo Now you can run the docker image:
-	@echo docker run --name "$(DOCKER_IMAGE_DEV_NAME)" -p 53:53/tcp -p 53:53/udp -p 80:80/tcp -p 443:443/tcp -p 853:853/tcp -p 3000:3000/tcp $(DOCKER_IMAGE_DEV_NAME)
+	@echo docker run --name "adguard-home" -p 53:53/tcp -p 53:53/udp -p 80:80/tcp -p 443:443/tcp -p 853:853/tcp -p 3000:3000/tcp $(DOCKER_IMAGE_NAME)
+
+lint:
+	@echo Running linters
+	golangci-lint run ./...
+	npm --prefix client run lint
+
+test:
+	@echo Running unit-tests
+	go test -race -v -bench=. -coverprofile=coverage.txt -covermode=atomic ./...
+
+ci: dependencies client test
+
+dependencies:
+	npm --prefix client ci
+	go mod download
 
 clean:
-	$(MAKE) cleanfast
+	# make build output
+	rm -f AdGuardHome
+	rm -f AdGuardHome.exe
+	# tests output
+	rm -rf data
+	rm -f coverage.txt
+	# static build output
 	rm -rf build
+	# dist folder
+	rm -rf $(DIST_DIR)
+	# client deps
 	rm -rf client/node_modules
+	# packr-generated files
+	PATH=$(GOPATH)/bin:$(PATH) packr clean || true
 
-cleanfast:
-	rm -f $(TARGET)
+docker-multi-arch:
+	DOCKER_CLI_EXPERIMENTAL=enabled \
+	docker buildx build \
+	--platform $(DOCKER_PLATFORMS) \
+	--build-arg VERSION=$(VERSION) \
+	--build-arg CHANNEL=$(CHANNEL) \
+	--build-arg VCS_REF=$(COMMIT) \
+	--build-arg BUILD_DATE=$(BUILD_DATE) \
+	$(DOCKER_TAGS) \
+	--output "$(DOCKER_OUTPUT)" \
+	-t "$(DOCKER_IMAGE_FULL_NAME)" -f ./Dockerfile .
+
+	@echo If the image was pushed to the registry, you can now run it:
+	@echo docker run --name "adguard-home" -p 53:53/tcp -p 53:53/udp -p 80:80/tcp -p 443:443/tcp -p 853:853/tcp -p 3000:3000/tcp $(DOCKER_IMAGE_NAME)
+
+release: dependencies client
+	@echo Starting release build: version $(VERSION), channel $(CHANNEL)
+	CHANNEL=$(CHANNEL) $(GORELEASER_COMMAND)
+	$(call write_version_file,$(VERSION))
+	PATH=$(GOPATH)/bin:$(PATH) packr clean
+
+define write_version_file
+	$(eval version := $(1))
+
+	@echo Writing version file: $(version)
+
+	# Variables for CI
+	rm -f $(DIST_DIR)/version.txt
+	echo "version=$(version)" > $(DIST_DIR)/version.txt
+
+	# Prepare the version.json file
+	rm -f $(DIST_DIR)/version.json
+	echo "{" >> $(DIST_DIR)/version.json
+	echo "  \"version\": \"$(version)\"," >> $(DIST_DIR)/version.json
+	echo "  \"announcement\": \"AdGuard Home $(version) is now available!\"," >> $(DIST_DIR)/version.json
+	echo "  \"announcement_url\": \"https://github.com/AdguardTeam/AdGuardHome/releases\"," >> $(DIST_DIR)/version.json
+	echo "  \"selfupdate_min_version\": \"v0.0\"," >> $(DIST_DIR)/version.json
+
+	# Windows builds
+	echo "  \"download_windows_amd64\": \"$(BASE_URL)/AdGuardHome_windows_amd64.zip\"," >> $(DIST_DIR)/version.json
+	echo "  \"download_windows_386\": \"$(BASE_URL)/AdGuardHome_windows_386.zip\"," >> $(DIST_DIR)/version.json
+
+	# MacOS builds
+	echo "  \"download_darwin_amd64\": \"$(BASE_URL)/AdGuardHome_darwin_amd64.zip\"," >> $(DIST_DIR)/version.json
+	echo "  \"download_darwin_386\": \"$(BASE_URL)/AdGuardHome_darwin_386.zip\"," >> $(DIST_DIR)/version.json
+
+	# Linux
+	echo "  \"download_linux_amd64\": \"$(BASE_URL)/AdGuardHome_linux_amd64.tar.gz\"," >> $(DIST_DIR)/version.json
+	echo "  \"download_linux_386\": \"$(BASE_URL)/AdGuardHome_linux_386.tar.gz\"," >> $(DIST_DIR)/version.json
+
+	# Linux, all kinds of ARM
+	echo "  \"download_linux_arm\": \"$(BASE_URL)/AdGuardHome_linux_armv6.tar.gz\"," >> $(DIST_DIR)/version.json
+	echo "  \"download_linux_armv5\": \"$(BASE_URL)/AdGuardHome_linux_armv5.tar.gz\"," >> $(DIST_DIR)/version.json
+	echo "  \"download_linux_armv6\": \"$(BASE_URL)/AdGuardHome_linux_armv6.tar.gz\"," >> $(DIST_DIR)/version.json
+	echo "  \"download_linux_armv7\": \"$(BASE_URL)/AdGuardHome_linux_armv7.tar.gz\"," >> $(DIST_DIR)/version.json
+	echo "  \"download_linux_arm64\": \"$(BASE_URL)/AdGuardHome_linux_arm64.tar.gz\"," >> $(DIST_DIR)/version.json
+
+	# Linux, MIPS
+	echo "  \"download_linux_mips\": \"$(BASE_URL)/AdGuardHome_linux_mips_softfloat.tar.gz\"," >> $(DIST_DIR)/version.json
+	echo "  \"download_linux_mipsle\": \"$(BASE_URL)/AdGuardHome_linux_mipsle_softfloat.tar.gz\"," >> $(DIST_DIR)/version.json
+	echo "  \"download_linux_mips64\": \"$(BASE_URL)/AdGuardHome_linux_mips64_softfloat.tar.gz\"," >> $(DIST_DIR)/version.json
+	echo "  \"download_linux_mips64le\": \"$(BASE_URL)/AdGuardHome_linux_mips64le_softfloat.tar.gz\"," >> $(DIST_DIR)/version.json
+
+	# FreeBSD
+	echo "  \"download_freebsd_386\": \"$(BASE_URL)/AdGuardHome_freebsd_386.tar.gz\"," >> $(DIST_DIR)/version.json
+	echo "  \"download_freebsd_amd64\": \"$(BASE_URL)/AdGuardHome_freebsd_amd64.tar.gz\"," >> $(DIST_DIR)/version.json
+
+	# FreeBSD, all kinds of ARM
+	echo "  \"download_freebsd_arm\": \"$(BASE_URL)/AdGuardHome_freebsd_armv6.tar.gz\"," >> $(DIST_DIR)/version.json
+	echo "  \"download_freebsd_armv5\": \"$(BASE_URL)/AdGuardHome_freebsd_armv5.tar.gz\"," >> $(DIST_DIR)/version.json
+	echo "  \"download_freebsd_armv6\": \"$(BASE_URL)/AdGuardHome_freebsd_armv6.tar.gz\"," >> $(DIST_DIR)/version.json
+	echo "  \"download_freebsd_armv7\": \"$(BASE_URL)/AdGuardHome_freebsd_armv7.tar.gz\"," >> $(DIST_DIR)/version.json
+	echo "  \"download_freebsd_arm64\": \"$(BASE_URL)/AdGuardHome_freebsd_arm64.tar.gz\"" >> $(DIST_DIR)/version.json
+
+	# Finish
+	echo "}" >> $(DIST_DIR)/version.json
+endef
\ No newline at end of file
diff --git a/README.md b/README.md
index 83aa32fb..17421a5d 100644
--- a/README.md
+++ b/README.md
@@ -14,9 +14,6 @@
     <a href="https://twitter.com/AdGuard">Twitter</a> |
     <a href="https://t.me/adguard_en">Telegram</a>
     <br /><br />
-    <a href="https://travis-ci.com/AdguardTeam/AdGuardHome">
-      <img src="https://travis-ci.com/AdguardTeam/AdGuardHome.svg" alt="Build status" />
-    </a>
     <a href="https://codecov.io/github/AdguardTeam/AdGuardHome?branch=master">
       <img src="https://img.shields.io/codecov/c/github/AdguardTeam/AdGuardHome/master.svg" alt="Code Coverage" />
     </a>
@@ -153,17 +150,11 @@ Is there a chance to handle this in the future? DNS will never be enough to do t
 
 ### Prerequisites
 
-You will need:
+You will need this to build AdGuard Home:
 
  * [go](https://golang.org/dl/) v1.14 or later.
  * [node.js](https://nodejs.org/en/download/) v10 or later.
 
-You can either install them via the provided links or use [brew.sh](https://brew.sh/) if you're on Mac:
-
-```bash
-brew install go node
-```
-
 ### Building
 
 Open Terminal and execute these commands:
@@ -174,15 +165,38 @@ cd AdGuardHome
 make
 ```
 
-#### (For devs) Upload translations
-```
-node upload.js
-```
+Check the [`Makefile`](https://github.com/AdguardTeam/AdGuardHome/blob/master/Makefile) to learn about other commands.
 
-#### (For devs) Download translations
-```
-node download.js
-```
+#### Preparing release
+
+You'll need this to prepare a release build:
+
+* [goreleaser](https://goreleaser.com/)
+* [snapcraft](https://snapcraft.io/)
+
+Commands:
+
+* `make release` - builds a snapshot build (CHANNEL=edge)
+* `CHANNEL=beta make release` - builds beta version, tag is mandatory.
+* `CHANNEL=release make release` - builds release version, tag is mandatory.
+
+#### Docker image
+
+* Run `make docker` to build the Docker image locally.
+* Run `make docker-multi-arch` to build the multi-arch Docker image (the one that we publish to Docker Hub).
+
+Please note, that we're using [Docker Buildx](https://docs.docker.com/buildx/working-with-buildx/) to build our official image.
+
+You may need to prepare before using these builds:
+
+* (Linux-only) Install Qemu: `docker run --rm --privileged multiarch/qemu-user-static --reset -p yes --credential yes`
+* Prepare builder: `docker buildx create --name buildx-builder --driver docker-container --use`
+
+
+### Resources that we update periodically
+
+* `scripts/translations`
+* `scripts/whotracksme`
 
 <a id="contributing"></a>
 ## Contributing
@@ -192,26 +206,41 @@ You are welcome to fork this repository, make your changes and submit a pull req
 <a id="test-unstable-versions"></a>
 ### Test unstable versions
 
+There are two update channels that you can use:
+
+* `beta` - beta version of AdGuard Home. More or less stable versions.
+* `edge` - the newest version of AdGuard Home. New updates are pushed to this channel daily and it is the closest to the master branch you can get.
+
+There are three options how you can install an unstable version:
+
+1. [Snap Store](https://snapcraft.io/adguard-home) -- look for "beta" and "edge" channels there.
+2. [Docker Hub](https://hub.docker.com/r/adguard/adguardhome) -- look for "beta" and "edge" tags there.
+3. Standalone builds. Look for the available builds below.
+
 There are three options how you can install an unstable version.
 
 1. You can either install a beta version of AdGuard Home which we update periodically.
 2. You can use the Docker image from the `edge` tag, which is synced with the repo master branch.
 3. You can install AdGuard Home from `beta` or `edge` channels on the Snap Store.
 
-* Beta builds
-    * [Raspberry Pi (32-bit ARMv6)](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_arm.tar.gz)
-    * [MacOS](https://static.adguard.com/adguardhome/beta/AdGuardHome_MacOS.zip)
-    * [Windows 64-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_Windows_amd64.zip)
-    * [Windows 32-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_Windows_386.zip)
-    * [Linux 64-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_amd64.tar.gz)
-    * [Linux 32-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_386.tar.gz)
-    * [FreeBSD 64-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_freebsd_amd64.tar.gz)
-    * [Linux 64-bit ARM](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_arm64.tar.gz)
-    * [Linux 32-bit ARMv5](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_armv5.tar.gz)
-    * [MIPS](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_mips.tar.gz)
-    * [MIPSLE](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_mipsle.tar.gz)
-* [Docker Hub](https://hub.docker.com/r/adguard/adguardhome)
-* [Snap Store](https://snapcraft.io/adguard-home)
+* Beta channel builds
+    * Linux: [64-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_amd64.tar.gz), [32-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_386.tar.gz)
+    * Linux ARM: [32-bit ARMv6](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_armv6.tar.gz) (recommended for Rapsberry Pi), [64-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_arm64.tar.gz), [32-bit ARMv5](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_armv5.tar.gz), [32-bit ARMv7](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_armv7.tar.gz)
+    * Linux MIPS: [32-bit MIPS](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_mips_softfloat.tar.gz), [32-bit MIPSLE](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_mipsle_softfloat.tar.gz), [64-bit MIPS](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_mips64_softfloat.tar.gz), [64-bit MIPSLE](https://static.adguard.com/adguardhome/beta/AdGuardHome_linux_mips64le_softfloat.tar.gz)
+    * Windows: [64-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_windows_amd64.zip), [32-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_windows_386.zip)
+    * MacOS: [64-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_darwin_amd64.zip), [32-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_darwin_386.zip)
+    * FreeBSD: [64-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_freebsd_amd64.tar.gz), [32-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_freebsd_386.tar.gz)
+    * FreeBSD ARM: [64-bit](https://static.adguard.com/adguardhome/beta/AdGuardHome_freebsd_arm64.tar.gz), [32-bit ARMv5](https://static.adguard.com/adguardhome/beta/AdGuardHome_freebsd_armv5.tar.gz), [32-bit ARMv6](https://static.adguard.com/adguardhome/beta/AdGuardHome_freebsd_armv6.tar.gz), [32-bit ARMv7](https://static.adguard.com/adguardhome/beta/AdGuardHome_freebsd_armv7.tar.gz)
+
+* Edge channel builds
+    * Linux: [64-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_amd64.tar.gz), [32-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_386.tar.gz)
+    * Linux ARM: [32-bit ARMv6](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_armv6.tar.gz) (recommended for Rapsberry Pi), [64-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_arm64.tar.gz), [32-bit ARMv5](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_armv5.tar.gz), [32-bit ARMv7](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_armv7.tar.gz)
+    * Linux MIPS: [32-bit MIPS](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_mips_softfloat.tar.gz), [32-bit MIPSLE](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_mipsle_softfloat.tar.gz), [64-bit MIPS](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_mips64_softfloat.tar.gz), [64-bit MIPSLE](https://static.adguard.com/adguardhome/edge/AdGuardHome_linux_mips64le_softfloat.tar.gz)
+    * Windows: [64-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_windows_amd64.zip), [32-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_windows_386.zip)
+    * MacOS: [64-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_darwin_amd64.zip), [32-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_darwin_386.zip)
+    * FreeBSD: [64-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_freebsd_amd64.tar.gz), [32-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_freebsd_386.tar.gz)
+    * FreeBSD ARM: [64-bit](https://static.adguard.com/adguardhome/edge/AdGuardHome_freebsd_arm64.tar.gz), [32-bit ARMv5](https://static.adguard.com/adguardhome/edge/AdGuardHome_freebsd_armv5.tar.gz), [32-bit ARMv6](https://static.adguard.com/adguardhome/edge/AdGuardHome_freebsd_armv6.tar.gz), [32-bit ARMv7](https://static.adguard.com/adguardhome/edge/AdGuardHome_freebsd_armv7.tar.gz)
+
 
 <a id="reporting-issues"></a>
 ### Report issues
diff --git a/build_docker.sh b/build_docker.sh
deleted file mode 100755
index 81173574..00000000
--- a/build_docker.sh
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/bin/env bash
-
-set -eE
-set -o pipefail
-set -x
-
-DOCKERFILE="packaging/docker/Dockerfile.hub"
-IMAGE_NAME="adguard/adguardhome"
-
-if [[ "${TRAVIS_BRANCH}" == "master" ]]
-then
-  VERSION="edge"
-else
-  VERSION=`git describe --abbrev=4 --dirty --always --tags`
-fi
-
-build_image() {
-    from="$(awk '$1 == toupper("FROM") { print $2 }' ${DOCKERFILE})"
-
-    # See https://hub.docker.com/r/multiarch/alpine/tags
-    case "${GOARCH}" in
-        arm64)
-           alpineArch='arm64-edge'
-           imageArch='arm64'
-           ;;
-        arm)
-           alpineArch='armhf-edge'
-           imageArch='armhf'
-           ;;
-        386)
-           alpineArch='i386-edge'
-           imageArch='i386'
-           ;;
-        amd64)
-           alpineArch='amd64-edge'
-           ;;
-        *)
-           alpineArch='amd64-edge'
-           ;;
-    esac
-
-    if [[ "${GOOS}" == "linux" ]] && [[ "${GOARCH}" == "amd64" ]]
-    then
-        image="${IMAGE_NAME}:${VERSION}"
-    else
-        image="${IMAGE_NAME}:${imageArch}-${VERSION}"
-    fi
-
-    make cleanfast; CGO_DISABLED=1 make
-
-    docker pull "multiarch/alpine:${alpineArch}"
-    docker tag "multiarch/alpine:${alpineArch}" "$from"
-    docker build -t "${image}" -f ${DOCKERFILE} .
-    docker push ${image}
-    if [[ "${VERSION}" != "edge" ]]
-    then
-        latest=${image/$VERSION/latest}
-        docker tag "${image}" "${latest}"
-        docker push ${latest}
-        docker rmi ${latest}
-    fi
-    docker rmi "$from"
-}
-
-# prepare qemu
-docker run --rm --privileged multiarch/qemu-user-static:register --reset
-
-make clean
-
-# Prepare releases
-GOOS=linux GOARCH=amd64 build_image
-GOOS=linux GOARCH=386 build_image
-GOOS=linux GOARCH=arm GOARM=6 build_image
-GOOS=linux GOARCH=arm64 GOARM=6 build_image
diff --git a/build_release.sh b/build_release.sh
deleted file mode 100755
index 4f2a4c42..00000000
--- a/build_release.sh
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/usr/bin/env bash
-
-set -eE
-set -o pipefail
-set -x
-
-channel=${1:-release}
-baseUrl="https://static.adguard.com/adguardhome/$channel"
-dst=dist
-version=`git describe --abbrev=4 --dirty --always --tags`
-
-f() {
-	make cleanfast; CGO_DISABLED=1 make
-	if [[ $GOOS == darwin ]]; then
-		zip $dst/AdGuardHome_MacOS.zip AdGuardHome README.md LICENSE.txt
-	elif [[ $GOOS == windows ]]; then
-		zip $dst/AdGuardHome_Windows_"$GOARCH".zip AdGuardHome.exe README.md LICENSE.txt
-	else
-		rm -rf dist/AdguardHome
-		mkdir -p dist/AdGuardHome
-		cp -pv {AdGuardHome,LICENSE.txt,README.md} dist/AdGuardHome/
-		pushd dist
-		if [[ $GOARCH == arm ]] && [[ $GOARM != 6 ]]; then
-			tar zcvf AdGuardHome_"$GOOS"_armv"$GOARM".tar.gz AdGuardHome/
-		else
-			tar zcvf AdGuardHome_"$GOOS"_"$GOARCH".tar.gz AdGuardHome/
-		fi
-		popd
-		rm -rf dist/AdguardHome
-	fi
-}
-
-# Clean dist and build
-make clean
-rm -rf $dst
-
-# Prepare the dist folder
-mkdir -p $dst
-
-# Prepare releases
-CHANNEL=$channel GOOS=darwin GOARCH=amd64 f
-CHANNEL=$channel GOOS=linux GOARCH=amd64 f
-CHANNEL=$channel GOOS=linux GOARCH=386 GO386=387 f
-CHANNEL=$channel GOOS=linux GOARCH=arm GOARM=5 f
-CHANNEL=$channel GOOS=linux GOARCH=arm GOARM=6 f
-CHANNEL=$channel GOOS=linux GOARCH=arm64 GOARM=6 f
-CHANNEL=$channel GOOS=windows GOARCH=amd64 f
-CHANNEL=$channel GOOS=windows GOARCH=386 f
-CHANNEL=$channel GOOS=linux GOARCH=mipsle GOMIPS=softfloat f
-CHANNEL=$channel GOOS=linux GOARCH=mips GOMIPS=softfloat f
-CHANNEL=$channel GOOS=freebsd GOARCH=amd64 f
-
-# Variables for CI
-echo "version=$version" > $dst/version.txt
-
-# Prepare the version.json file
-echo "{" >> $dst/version.json
-echo "  \"version\": \"$version\"," >> $dst/version.json
-echo "  \"announcement\": \"AdGuard Home $version is now available!\"," >> $dst/version.json
-echo "  \"announcement_url\": \"https://github.com/AdguardTeam/AdGuardHome/releases\"," >> $dst/version.json
-echo "  \"download_windows_amd64\": \"$baseUrl/AdGuardHome_Windows_amd64.zip\"," >> $dst/version.json
-echo "  \"download_windows_386\": \"$baseUrl/AdGuardHome_Windows_386.zip\"," >> $dst/version.json
-echo "  \"download_darwin_amd64\": \"$baseUrl/AdGuardHome_MacOS.zip\"," >> $dst/version.json
-echo "  \"download_linux_amd64\": \"$baseUrl/AdGuardHome_linux_amd64.tar.gz\"," >> $dst/version.json
-echo "  \"download_linux_386\": \"$baseUrl/AdGuardHome_linux_386.tar.gz\"," >> $dst/version.json
-echo "  \"download_linux_arm\": \"$baseUrl/AdGuardHome_linux_arm.tar.gz\"," >> $dst/version.json
-echo "  \"download_linux_armv5\": \"$baseUrl/AdGuardHome_linux_armv5.tar.gz\"," >> $dst/version.json
-echo "  \"download_linux_arm64\": \"$baseUrl/AdGuardHome_linux_arm64.tar.gz\"," >> $dst/version.json
-echo "  \"download_linux_mips\": \"$baseUrl/AdGuardHome_linux_mips.tar.gz\"," >> $dst/version.json
-echo "  \"download_linux_mipsle\": \"$baseUrl/AdGuardHome_linux_mipsle.tar.gz\"," >> $dst/version.json
-echo "  \"download_freebsd_amd64\": \"$baseUrl/AdGuardHome_freebsd_amd64.tar.gz\"," >> $dst/version.json
-echo "  \"selfupdate_min_version\": \"v0.0\"" >> $dst/version.json
-echo "}" >> $dst/version.json
diff --git a/build_snap.sh b/build_snap.sh
deleted file mode 100755
index 13dae65f..00000000
--- a/build_snap.sh
+++ /dev/null
@@ -1,292 +0,0 @@
-#!/usr/bin/env bash
-
-set -eE
-set -o pipefail
-set -x
-
-BUILDER_IMAGE="adguard/snapcraft:1.0"
-SNAPCRAFT_TMPL="packaging/snap/snapcraft.yaml"
-SNAP_NAME="adguard-home"
-LAUNCHPAD_CREDENTIALS_DIR=".local/share/snapcraft/provider/launchpad"
-
-if [[ -z ${VERSION} ]]; then
-    VERSION=$(git describe --abbrev=4 --dirty --always --tags)
-    echo "VERSION env variable is not set, getting it from git: ${VERSION}"
-fi
-
-# If bash is interactive, set `-it` parameter for docker run
-INTERACTIVE=""
-if [ -t 0 ]; then
-    INTERACTIVE="-it"
-fi
-
-function usage() {
-    cat <<EOF
-    Usage: ${0##*/} command [options]
-
-    Please note that in order for the builds to work properly, you need to setup some env variables.
-
-    These are necessary for "remote-build' command.
-    Read this doc on how to generate them: https://uci.readthedocs.io/en/latest/oauth.html
-
-        * LAUNCHPAD_KEY -- launchpad CI key
-        * LAUNCHPAD_ACCESS_TOKEN -- launchpad access token
-        * LAUNCHPAD_ACCESS_SECRET -- launchpad access secret
-
-    These are necessary for snapcraft publish command to work.
-    They can be exported using "snapcraft export-login"
-
-        * SNAPCRAFT_MACAROON
-        * SNAPCRAFT_UBUNTU_DISCHARGE
-        * SNAPCRAFT_EMAIL
-
-    Examples:
-        ${0##*/} build-docker - builds snaps using remote-build inside a Docker environment
-        ${0##*/} build - builds snaps using remote-build
-        ${0##*/} publish-docker-beta - publishes snaps to the beta channel using Docker environment
-        ${0##*/} publish-docker-release - publishes snaps to the release channel using Docker environment
-        ${0##*/} publish-beta - publishes snaps to the beta channel
-        ${0##*/} publish-release - publishes snaps to the release channel
-        ${0##*/} cleanup - clean up temporary files that were created by the builds
-EOF
-    exit 1
-}
-
-#######################################
-# helper functions
-#######################################
-
-function prepare() {
-    if [ -z "${LAUNCHPAD_KEY}" ] || [ -z "${LAUNCHPAD_ACCESS_TOKEN}" ] || [ -z "${LAUNCHPAD_ACCESS_SECRET}" ]; then
-        echo "Launchpad oauth tokens are not set, exiting"
-        usage
-        exit 1
-    fi
-
-    if [ -z "${SNAPCRAFT_MACAROON}" ] || [ -z "${SNAPCRAFT_UBUNTU_DISCHARGE}" ] || [ -z "${SNAPCRAFT_EMAIL}" ]; then
-        echo "Snapcraft auth params are not set, exiting"
-        usage
-        exit 1
-    fi
-
-    # Launchpad oauth tokens data is necessary to run snapcraft remote-build
-    #
-    # Here's an instruction on how to generate launchpad OAuth tokens:
-    # https://uci.readthedocs.io/en/latest/oauth.html
-    #
-    # Launchpad credentials are necessary to run snapcraft remote-build command
-    echo "[1]
-    consumer_key = ${LAUNCHPAD_KEY}
-    consumer_secret =
-    access_token = ${LAUNCHPAD_ACCESS_TOKEN}
-    access_secret = ${LAUNCHPAD_ACCESS_SECRET}
-    " >launchpad_credentials
-
-    # Snapcraft login data
-    # It can be exported using snapcraft export-login command
-    echo "[login.ubuntu.com]
-    macaroon = ${SNAPCRAFT_MACAROON}
-    unbound_discharge = ${SNAPCRAFT_UBUNTU_DISCHARGE}
-    email = ${SNAPCRAFT_EMAIL}" >snapcraft_login
-
-    # Prepare the snap configuration
-    cp ${SNAPCRAFT_TMPL} ./snapcraft.yaml
-    sed -i.bak 's/dev_version/'"${VERSION}"'/g' ./snapcraft.yaml
-    rm -f snapcraft.yaml.bak
-}
-
-build_snap() {
-    # prepare credentials
-    prepare
-
-    # copy them to the directory where snapcraft will be able to read them
-    mkdir -p ~/${LAUNCHPAD_CREDENTIALS_DIR}
-    cp -f snapcraft_login ~/${LAUNCHPAD_CREDENTIALS_DIR}/credentials
-    chmod 600 ~/${LAUNCHPAD_CREDENTIALS_DIR}/credentials
-
-    # run the build
-    snapcraft remote-build --build-on=${ARCH} --launchpad-accept-public-upload
-
-    # remove the credentials - we don't need them anymore
-    rm -rf ~/${LAUNCHPAD_CREDENTIALS_DIR}
-
-    # remove version from the file name
-    rename_snap_file
-
-    # cleanup credentials
-    cleanup
-}
-
-build_snap_docker() {
-    # prepare credentials
-    prepare
-
-    docker run ${INTERACTIVE} --rm \
-        -v $(pwd):/build \
-        -v $(pwd)/launchpad_credentials:/root/${LAUNCHPAD_CREDENTIALS_DIR}/credentials:ro \
-        ${BUILDER_IMAGE} \
-        snapcraft remote-build --build-on=${ARCH} --launchpad-accept-public-upload
-
-    # remove version from the file name
-    rename_snap_file
-
-    # cleanup credentials
-    cleanup
-}
-
-rename_snap_file() {
-    # In order to make working with snaps easier later on
-    # we remove version from the file name
-
-    # Check that the snap file exists
-    snapFile="${SNAP_NAME}_${VERSION}_${ARCH}.snap"
-    if [ ! -f ${snapFile} ]; then
-        echo "Snap file ${snapFile} not found!"
-        exit 1
-    fi
-
-    mv -f ${snapFile} "${SNAP_NAME}_${ARCH}.snap"
-}
-
-publish_snap() {
-    # prepare credentials
-    prepare
-
-    # Check that the snap file exists
-    snapFile="${SNAP_NAME}_${ARCH}.snap"
-    if [ ! -f ${snapFile} ]; then
-        echo "Snap file ${snapFile} not found!"
-        exit 1
-    fi
-
-    # Login if necessary
-    snapcraft login --with=snapcraft_login
-
-    # Push to the channel
-    snapcraft push --release=${CHANNEL} ${snapFile}
-
-    # cleanup credentials
-    cleanup
-}
-
-publish_snap_docker() {
-    # prepare credentials
-    prepare
-
-    # Check that the snap file exists
-    snapFile="${SNAP_NAME}_${ARCH}.snap"
-    if [ ! -f ${snapFile} ]; then
-        echo "Snap file ${snapFile} not found!"
-        exit 1
-    fi
-
-    # Login and publish the snap
-    docker run ${INTERACTIVE} --rm \
-        -v $(pwd):/build \
-        ${BUILDER_IMAGE} \
-        sh -c "snapcraft login --with=/build/snapcraft_login && snapcraft push --release=${CHANNEL} /build/${snapFile}"
-
-    # cleanup credentials
-    cleanup
-}
-
-#######################################
-# main functions
-#######################################
-
-build() {
-    if [[ -n "$1" ]]; then
-        echo "ARCH is set to $1"
-        ARCH=$1 build_snap
-    else
-        ARCH=i386 build_snap
-        ARCH=arm64 build_snap
-        ARCH=armhf build_snap
-        ARCH=amd64 build_snap
-    fi
-}
-
-build_docker() {
-    if [[ -n "$1" ]]; then
-        echo "ARCH is set to $1"
-        ARCH=$1 build_snap_docker
-    else
-        ARCH=i386 build_snap_docker
-        ARCH=arm64 build_snap_docker
-        ARCH=armhf build_snap_docker
-        ARCH=amd64 build_snap_docker
-    fi
-}
-
-publish_docker() {
-    if [[ -z $1 ]]; then
-        echo "No channel specified"
-        exit 1
-    fi
-    CHANNEL="${1}"
-    if [ "$CHANNEL" != "stable" ] && [ "$CHANNEL" != "beta" ]; then
-        echo "$CHANNEL is an invalid value for the update channel!"
-        exit 1
-    fi
-
-    if [[ -n "$2" ]]; then
-        echo "ARCH is set to $2"
-        ARCH=$2 publish_snap_docker
-    else
-        ARCH=i386 publish_snap_docker
-        ARCH=arm64 publish_snap_docker
-        ARCH=armhf publish_snap_docker
-        ARCH=amd64 publish_snap_docker
-    fi
-}
-
-publish() {
-    if [[ -z $1 ]]; then
-        echo "No channel specified"
-        exit 1
-    fi
-    CHANNEL="${1}"
-    if [ "$CHANNEL" != "stable" ] && [ "$CHANNEL" != "beta" ]; then
-        echo "$CHANNEL is an invalid value for the update channel!"
-        exit 1
-    fi
-
-    if [[ -n "$2" ]]; then
-        echo "ARCH is set to $2"
-        ARCH=$2 publish_snap
-    else
-        ARCH=i386 publish_snap
-        ARCH=arm64 publish_snap
-        ARCH=armhf publish_snap
-        ARCH=amd64 publish_snap
-    fi
-}
-
-cleanup() {
-    rm -f launchpad_credentials
-    rm -f snapcraft.yaml
-    rm -f snapcraft.yaml.bak
-    rm -f snapcraft_login
-    git checkout snapcraft.yaml
-}
-
-#######################################
-# main
-#######################################
-if [[ -z $1 || $1 == "--help" || $1 == "-h" ]]; then
-    usage
-fi
-
-case "$1" in
-"build-docker") build_docker $2 ;;
-"build") build $2 ;;
-"publish-docker-beta") publish_docker beta $2 ;;
-"publish-docker-release") publish_docker stable $2 ;;
-"publish-beta") publish beta $2 ;;
-"publish-release") publish stable $2 ;;
-"prepare") prepare ;;
-"cleanup") cleanup ;;
-*) usage ;;
-esac
-
-exit 0
diff --git a/ci.sh b/ci.sh
deleted file mode 100755
index 544f0109..00000000
--- a/ci.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-set -e
-set -x
-
-echo "Starting AdGuard Home CI script"
-
-# Print the current directory contents
-ls -la
-
-# Check versions and current directory
-node -v
-npm -v
-go version
-golangci-lint --version
-
-# Run linter
-golangci-lint run
-
-# Make
-make clean
-make build/static/index.html
-make
-
-# Run tests
-go test -race -v -bench=. -coverprofile=coverage.txt -covermode=atomic ./...
-
-# if [[ -z "$(git status --porcelain)" ]]; then
-#     # Working directory clean
-#     echo "Git status is clean"
-# else
-#     echo "Git status is not clean and contains uncommited changes"
-#     echo "Please make sure there are no changes"
-#     exit 1
-# fi
-
-echo "AdGuard Home CI script finished successfully"
\ No newline at end of file
diff --git a/go.sum b/go.sum
index 6d05c0a3..19938814 100644
--- a/go.sum
+++ b/go.sum
@@ -1,6 +1,5 @@
 github.com/AdguardTeam/dnsproxy v0.29.1 h1:Stc+JLh67C9K38vbrH2920+3FnbXKkFzYQqRiu5auUo=
 github.com/AdguardTeam/dnsproxy v0.29.1/go.mod h1:hOYFV9TW+pd5XKYz7KZf2FFD8SvSPqjyGTxUae86s58=
-github.com/AdguardTeam/golibs v0.4.0 h1:4VX6LoOqFe9p9Gf55BeD8BvJD6M6RDYmgEiHrENE9KU=
 github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
 github.com/AdguardTeam/golibs v0.4.2 h1:7M28oTZFoFwNmp8eGPb3ImmYbxGaJLyQXeIFVHjME0o=
 github.com/AdguardTeam/golibs v0.4.2/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
@@ -42,15 +41,17 @@ github.com/go-test/deep v1.0.5 h1:AKODKU3pDH1RzZzm6YZu77YWtEAq6uh1rLIAQlay2qc=
 github.com/go-test/deep v1.0.5/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
 github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
 github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
+github.com/gobuffalo/logger v1.0.0 h1:xw9Ko9EcC5iAFprrjJ6oZco9UpzS5MQ4jAwghsLHdy4=
 github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
 github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
 github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
 github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
 github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
+github.com/gobuffalo/packr/v2 v2.5.1 h1:TFOeY2VoGamPjQLiNDT3mn//ytzk236VMO2j7iHxJR4=
 github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
 github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
@@ -58,13 +59,13 @@ github.com/joomcode/errorx v1.0.1 h1:CalpDWz14ZHd68fIqluJasJosAewpz2TFaJALrUxjrk
 github.com/joomcode/errorx v1.0.1/go.mod h1:kgco15ekB6cs+4Xjzo7SPeXzx38PbJzBwbnu9qfVNHQ=
 github.com/kardianos/service v1.0.0 h1:HgQS3mFfOlyntWX8Oke98JcJLqt1DBcHR4kxShpYef0=
 github.com/kardianos/service v1.0.0/go.mod h1:8CzDhVuCuugtsHyZoTvsOBuvonN/UDBvl0kH+BUxvbo=
+github.com/karrick/godirwalk v1.10.12 h1:BqUm+LuJcXjGv1d2mj3gBiQyrQ57a0rYoAmhvJQ7RDU=
 github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
 github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
@@ -90,11 +91,13 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/shirou/gopsutil v2.20.3+incompatible h1:0JVooMPsT7A7HqEYdydp/OfjSOYSjhXV7w1hkKj/NPQ=
 github.com/shirou/gopsutil v2.20.3+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/sparrc/go-ping v0.0.0-20190613174326-4e5b6552494c h1:gqEdF4VwBu3lTKGHS9rXE9x1/pEaSwCXRLOZRF6qtlw=
 github.com/sparrc/go-ping v0.0.0-20190613174326-4e5b6552494c/go.mod h1:eMyUVp6f/5jnzM+3zahzl7q6UXLbgSc3MKg/+ow9QW0=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
 github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
@@ -102,9 +105,7 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
 github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
@@ -112,9 +113,7 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
 go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
-golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0=
 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -124,37 +123,32 @@ golang.org/x/crypto v0.0.0-20200403201458-baeed622b8d8/go.mod h1:LzIPMQfyMNhhGPh
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
 golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
 golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
 golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200331124033-c3d80250170d h1:nc5K6ox/4lTFbMVSL9WRR81ixkcwXThoiF6yf+R9scA=
 golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
 golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
diff --git a/home/control_update.go b/home/control_update.go
index 6115fac1..d734f4d1 100644
--- a/home/control_update.go
+++ b/home/control_update.go
@@ -18,61 +18,20 @@ import (
 	"time"
 
 	"github.com/AdguardTeam/AdGuardHome/util"
-
 	"github.com/AdguardTeam/golibs/log"
 )
 
-// Convert version.json data to our JSON response
-func getVersionResp(data []byte) []byte {
-	versionJSON := make(map[string]interface{})
-	err := json.Unmarshal(data, &versionJSON)
-	if err != nil {
-		log.Error("version.json: %s", err)
-		return []byte{}
-	}
-
-	ret := make(map[string]interface{})
-	ret["can_autoupdate"] = false
-
-	var ok1, ok2, ok3 bool
-	ret["new_version"], ok1 = versionJSON["version"].(string)
-	ret["announcement"], ok2 = versionJSON["announcement"].(string)
-	ret["announcement_url"], ok3 = versionJSON["announcement_url"].(string)
-	selfUpdateMinVersion, ok4 := versionJSON["selfupdate_min_version"].(string)
-	if !ok1 || !ok2 || !ok3 || !ok4 {
-		log.Error("version.json: invalid data")
-		return []byte{}
-	}
-
-	// the key is download_linux_arm or download_linux_arm64 for regular ARM versions
-	dloadName := fmt.Sprintf("download_%s_%s", runtime.GOOS, runtime.GOARCH)
-	if runtime.GOARCH == "arm" && ARMVersion == "5" {
-		// the key is download_linux_armv5 for ARMv5
-		dloadName = fmt.Sprintf("download_%s_%sv%s", runtime.GOOS, runtime.GOARCH, ARMVersion)
-	}
-	_, ok := versionJSON[dloadName]
-	if ok && ret["new_version"] != versionString && versionString >= selfUpdateMinVersion {
-		canUpdate := true
-
-		tlsConf := tlsConfigSettings{}
-		Context.tls.WriteDiskConfig(&tlsConf)
-
-		if runtime.GOOS != "windows" &&
-			((tlsConf.Enabled && (tlsConf.PortHTTPS < 1024 || tlsConf.PortDNSOverTLS < 1024)) ||
-				config.BindPort < 1024 ||
-				config.DNS.Port < 1024) {
-			// On UNIX, if we're running under a regular user,
-			//  but with CAP_NET_BIND_SERVICE set on a binary file,
-			//  and we're listening on ports <1024,
-			//  we won't be able to restart after we replace the binary file,
-			//  because we'll lose CAP_NET_BIND_SERVICE capability.
-			canUpdate, _ = util.HaveAdminRights()
-		}
-		ret["can_autoupdate"] = canUpdate
-	}
-
-	d, _ := json.Marshal(ret)
-	return d
+type updateInfo struct {
+	pkgURL           string // URL for the new package
+	pkgName          string // Full path to package file
+	newVer           string // New version string
+	updateDir        string // Full path to the directory containing unpacked files from the new package
+	backupDir        string // Full path to backup directory
+	configName       string // Full path to the current configuration file
+	updateConfigName string // Full path to the configuration file to check by the new binary
+	curBinName       string // Full path to the current executable file
+	bkpBinName       string // Full path to the current executable file in backup directory
+	newBinName       string // Full path to the new executable file
 }
 
 type getVersionJSONRequest struct {
@@ -81,16 +40,18 @@ type getVersionJSONRequest struct {
 
 // Get the latest available version from the Internet
 func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) {
-
 	if Context.disableUpdate {
 		return
 	}
 
 	req := getVersionJSONRequest{}
-	err := json.NewDecoder(r.Body).Decode(&req)
-	if err != nil {
-		httpError(w, http.StatusBadRequest, "JSON parse: %s", err)
-		return
+	var err error
+	if r.ContentLength != 0 {
+		err = json.NewDecoder(r.Body).Decode(&req)
+		if err != nil {
+			httpError(w, http.StatusBadRequest, "JSON parse: %s", err)
+			return
+		}
 	}
 
 	now := time.Now()
@@ -103,7 +64,7 @@ func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) {
 		if cached {
 			log.Tracef("Returning cached data")
 			w.Header().Set("Content-Type", "application/json")
-			w.Write(getVersionResp(data))
+			_, _ = w.Write(getVersionResp(data))
 			return
 		}
 	}
@@ -146,6 +107,80 @@ func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) {
 	}
 }
 
+// Perform an update procedure to the latest available version
+func handleUpdate(w http.ResponseWriter, r *http.Request) {
+	if len(config.versionCheckJSON) == 0 {
+		httpError(w, http.StatusBadRequest, "/update request isn't allowed now")
+		return
+	}
+
+	u, err := getUpdateInfo(config.versionCheckJSON)
+	if err != nil {
+		httpError(w, http.StatusInternalServerError, "%s", err)
+		return
+	}
+
+	err = doUpdate(u)
+	if err != nil {
+		httpError(w, http.StatusInternalServerError, "%s", err)
+		return
+	}
+
+	returnOK(w)
+	if f, ok := w.(http.Flusher); ok {
+		f.Flush()
+	}
+
+	go finishUpdate(u)
+}
+
+// Convert version.json data to our JSON response
+func getVersionResp(data []byte) []byte {
+	versionJSON := make(map[string]interface{})
+	err := json.Unmarshal(data, &versionJSON)
+	if err != nil {
+		log.Error("version.json: %s", err)
+		return []byte{}
+	}
+
+	ret := make(map[string]interface{})
+	ret["can_autoupdate"] = false
+
+	var ok1, ok2, ok3 bool
+	ret["new_version"], ok1 = versionJSON["version"].(string)
+	ret["announcement"], ok2 = versionJSON["announcement"].(string)
+	ret["announcement_url"], ok3 = versionJSON["announcement_url"].(string)
+	selfUpdateMinVersion, ok4 := versionJSON["selfupdate_min_version"].(string)
+	if !ok1 || !ok2 || !ok3 || !ok4 {
+		log.Error("version.json: invalid data")
+		return []byte{}
+	}
+
+	_, ok := getDownloadURL(versionJSON)
+	if ok && ret["new_version"] != versionString && versionString >= selfUpdateMinVersion {
+		canUpdate := true
+
+		tlsConf := tlsConfigSettings{}
+		Context.tls.WriteDiskConfig(&tlsConf)
+
+		if runtime.GOOS != "windows" &&
+			((tlsConf.Enabled && (tlsConf.PortHTTPS < 1024 || tlsConf.PortDNSOverTLS < 1024)) ||
+				config.BindPort < 1024 ||
+				config.DNS.Port < 1024) {
+			// On UNIX, if we're running under a regular user,
+			//  but with CAP_NET_BIND_SERVICE set on a binary file,
+			//  and we're listening on ports <1024,
+			//  we won't be able to restart after we replace the binary file,
+			//  because we'll lose CAP_NET_BIND_SERVICE capability.
+			canUpdate, _ = util.HaveAdminRights()
+		}
+		ret["can_autoupdate"] = canUpdate
+	}
+
+	d, _ := json.Marshal(ret)
+	return d
+}
+
 // Copy file on disk
 func copyFile(src, dst string) error {
 	d, e := ioutil.ReadFile(src)
@@ -159,19 +194,6 @@ func copyFile(src, dst string) error {
 	return nil
 }
 
-type updateInfo struct {
-	pkgURL           string // URL for the new package
-	pkgName          string // Full path to package file
-	newVer           string // New version string
-	updateDir        string // Full path to the directory containing unpacked files from the new package
-	backupDir        string // Full path to backup directory
-	configName       string // Full path to the current configuration file
-	updateConfigName string // Full path to the configuration file to check by the new binary
-	curBinName       string // Full path to the current executable file
-	bkpBinName       string // Full path to the current executable file in backup directory
-	newBinName       string // Full path to the new executable file
-}
-
 // Fill in updateInfo object
 func getUpdateInfo(jsonData []byte) (*updateInfo, error) {
 	var u updateInfo
@@ -184,7 +206,12 @@ func getUpdateInfo(jsonData []byte) (*updateInfo, error) {
 		return nil, fmt.Errorf("JSON parse: %s", err)
 	}
 
-	u.pkgURL = versionJSON[fmt.Sprintf("download_%s_%s", runtime.GOOS, runtime.GOARCH)].(string)
+	pkgURL, ok := getDownloadURL(versionJSON)
+	if !ok {
+		return nil, fmt.Errorf("failed to get download URL")
+	}
+
+	u.pkgURL = pkgURL
 	u.newVer = versionJSON["version"].(string)
 	if len(u.pkgURL) == 0 || len(u.newVer) == 0 {
 		return nil, fmt.Errorf("invalid JSON")
@@ -226,6 +253,33 @@ func getUpdateInfo(jsonData []byte) (*updateInfo, error) {
 	return &u, nil
 }
 
+// getDownloadURL - gets download URL for the current GOOS/GOARCH
+// returns
+func getDownloadURL(json map[string]interface{}) (string, bool) {
+	var key string
+
+	if runtime.GOARCH == "arm" && ARMVersion != "" {
+		// the key is:
+		// download_linux_armv5 for ARMv5
+		// download_linux_armv6 for ARMv6
+		// download_linux_armv7 for ARMv7
+		key = fmt.Sprintf("download_%s_%sv%s", runtime.GOOS, runtime.GOARCH, ARMVersion)
+	}
+
+	u, ok := json[key]
+	if !ok {
+		// the key is download_linux_arm or download_linux_arm64 for regular ARM versions
+		key = fmt.Sprintf("download_%s_%s", runtime.GOOS, runtime.GOARCH)
+		u, ok = json[key]
+	}
+
+	if !ok {
+		return "", false
+	}
+
+	return u.(string), true
+}
+
 // Unpack all files from .zip file to the specified directory
 // Existing files are overwritten
 // Return the list of files (not directories) written
@@ -526,31 +580,3 @@ func finishUpdate(u *updateInfo) {
 		// Unreachable code
 	}
 }
-
-// Perform an update procedure to the latest available version
-func handleUpdate(w http.ResponseWriter, r *http.Request) {
-
-	if len(config.versionCheckJSON) == 0 {
-		httpError(w, http.StatusBadRequest, "/update request isn't allowed now")
-		return
-	}
-
-	u, err := getUpdateInfo(config.versionCheckJSON)
-	if err != nil {
-		httpError(w, http.StatusInternalServerError, "%s", err)
-		return
-	}
-
-	err = doUpdate(u)
-	if err != nil {
-		httpError(w, http.StatusInternalServerError, "%s", err)
-		return
-	}
-
-	returnOK(w)
-	if f, ok := w.(http.Flusher); ok {
-		f.Flush()
-	}
-
-	go finishUpdate(u)
-}
diff --git a/home/home.go b/home/home.go
index 077455e8..79cf0843 100644
--- a/home/home.go
+++ b/home/home.go
@@ -134,6 +134,15 @@ func Main(version string, channel string, armVer string) {
 	run(args)
 }
 
+// version - returns the current version string
+func version() string {
+	msg := "AdGuard Home, version %s, channel %s, arch %s %s"
+	if ARMVersion != "" {
+		msg = msg + " v" + ARMVersion
+	}
+	return fmt.Sprintf(msg, versionString, updateChannel, runtime.GOOS, runtime.GOARCH)
+}
+
 // run initializes configuration and runs the AdGuard Home
 // run is a blocking method!
 // nolint
@@ -153,11 +162,7 @@ func run(args options) {
 	configureLogger(args)
 
 	// print the first message after logger is configured
-	msg := "AdGuard Home, version %s, channel %s, arch %s %s"
-	if ARMVersion != "" {
-		msg = msg + " v" + ARMVersion
-	}
-	log.Printf(msg, versionString, updateChannel, runtime.GOOS, runtime.GOARCH)
+	log.Println(version())
 	log.Debug("Current working directory is %s", Context.workDir)
 	if args.runningAsService {
 		log.Info("AdGuard Home is running as a service")
@@ -564,7 +569,7 @@ func loadOptions() options {
 		{"verbose", "v", "Enable verbose output", nil, func() { o.verbose = true }},
 		{"glinet", "", "Run in GL-Inet compatibility mode", nil, func() { o.glinetMode = true }},
 		{"version", "", "Show the version and exit", nil, func() {
-			fmt.Printf("AdGuardHome %s\n", versionString)
+			fmt.Println(version())
 			os.Exit(0)
 		}},
 		{"help", "", "Print this help", nil, func() {
diff --git a/main.go b/main.go
index 449e3834..eebcd5c4 100644
--- a/main.go
+++ b/main.go
@@ -1,3 +1,6 @@
+//go:generate go install -v github.com/gobuffalo/packr/packr
+//go:generate packr clean
+//go:generate packr -z
 package main
 
 import (
diff --git a/packaging/docker/Dockerfile b/packaging/docker/Dockerfile
deleted file mode 100644
index 6677d928..00000000
--- a/packaging/docker/Dockerfile
+++ /dev/null
@@ -1,32 +0,0 @@
-FROM golang:alpine AS build
-
-RUN apk add --update git make build-base npm && \
-    rm -rf /var/cache/apk/*
-
-WORKDIR /src/AdGuardHome
-COPY . /src/AdGuardHome
-RUN make
-
-FROM alpine:latest
-LABEL maintainer="AdGuard Team <devteam@adguard.com>"
-
-# Update CA certs
-RUN apk --no-cache --update add ca-certificates libcap && \
-    rm -rf /var/cache/apk/* && \
-    mkdir -p /opt/adguardhome/conf /opt/adguardhome/work && \
-    chown -R nobody: /opt/adguardhome
-
-COPY --from=build --chown=nobody:nogroup /src/AdGuardHome/AdGuardHome /opt/adguardhome/AdGuardHome
-
-RUN setcap 'cap_net_bind_service=+eip' /opt/adguardhome/AdGuardHome
-
-EXPOSE 53/tcp 53/udp 67/udp 68/udp 80/tcp 443/tcp 853/tcp 3000/tcp
-
-VOLUME ["/opt/adguardhome/conf", "/opt/adguardhome/work"]
-
-WORKDIR /opt/adguardhome/work
-
-#USER nobody
-
-ENTRYPOINT ["/opt/adguardhome/AdGuardHome"]
-CMD ["-c", "/opt/adguardhome/conf/AdGuardHome.yaml", "-w", "/opt/adguardhome/work", "--no-check-update"]
diff --git a/packaging/docker/Dockerfile.hub b/packaging/docker/Dockerfile.hub
deleted file mode 100644
index 0e11574e..00000000
--- a/packaging/docker/Dockerfile.hub
+++ /dev/null
@@ -1,23 +0,0 @@
-FROM alpine:latest
-LABEL maintainer="AdGuard Team <devteam@adguard.com>"
-
-# Update CA certs
-RUN apk --no-cache --update add ca-certificates libcap && \
-    rm -rf /var/cache/apk/* && \
-    mkdir -p /opt/adguardhome/conf /opt/adguardhome/work && \
-    chown -R nobody: /opt/adguardhome
-
-COPY --chown=nobody:nogroup ./AdGuardHome /opt/adguardhome/AdGuardHome
-
-RUN setcap 'cap_net_bind_service=+eip' /opt/adguardhome/AdGuardHome
-
-EXPOSE 53/tcp 53/udp 67/udp 68/udp 80/tcp 443/tcp 853/tcp 3000/tcp
-
-VOLUME ["/opt/adguardhome/conf", "/opt/adguardhome/work"]
-
-WORKDIR /opt/adguardhome/work
-
-#USER nobody
-
-ENTRYPOINT ["/opt/adguardhome/AdGuardHome"]
-CMD ["-h", "0.0.0.0", "-c", "/opt/adguardhome/conf/AdGuardHome.yaml", "-w", "/opt/adguardhome/work", "--no-check-update"]
diff --git a/packaging/docker/README.md b/packaging/docker/README.md
deleted file mode 100644
index 0d7ff06f..00000000
--- a/packaging/docker/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-## Docker images
-
-* `Dockerfile` is used for local development. Build it using `make docker` command.
-
-* `Dockerfile.hub` is used to publish AdGuard images to Docker Hub: https://hub.docker.com/r/adguard/adguardhome
-    Check out `build_docker.sh` for the details.
\ No newline at end of file
diff --git a/packaging/snap/README.md b/packaging/snap/README.md
deleted file mode 100644
index 764100ff..00000000
--- a/packaging/snap/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-## Snapcraft
-
-Configuration for our snap.
-
-Check out `build_snap.sh` for more details.
\ No newline at end of file
diff --git a/packaging/snap/snapcraft.yaml b/packaging/snap/snapcraft.yaml
deleted file mode 100644
index 1d6c5274..00000000
--- a/packaging/snap/snapcraft.yaml
+++ /dev/null
@@ -1,32 +0,0 @@
-name: adguard-home
-base: core18
-version: 'dev_version'
-summary: Network-wide ads & trackers blocking DNS server
-description: |
-  AdGuard Home is a network-wide software for blocking ads & tracking. After
-  you set it up, it'll cover ALL your home devices, and you don't need any
-  client-side software for that.
-
-  It operates as a DNS server that re-routes tracking domains to a "black hole,"
-  thus preventing your devices from connecting to those servers. It's based
-  on software we use for our public AdGuard DNS servers -- both share a lot
-  of common code.
-grade: stable
-confinement: strict
-
-parts:
-  adguard-home:
-    plugin: make
-    source: .
-    build-snaps: [ node/13/stable, go ]
-    build-packages: [ git, build-essential ]
-    override-build: |
-      make clean
-      make
-      cp AdGuardHome ${SNAPCRAFT_PART_INSTALL}/
-apps:
-  adguard-home:
-    command: AdGuardHome -w ${SNAP_DATA} --no-check-update
-    plugs: [ network-bind ]
-    daemon: simple
-    restart-condition: always
\ No newline at end of file
diff --git a/snapcraft.yaml b/snapcraft.yaml
deleted file mode 100644
index c5cda82b..00000000
--- a/snapcraft.yaml
+++ /dev/null
@@ -1,41 +0,0 @@
-# Note that this snapcraft.yaml file is used for automatic Edge channel builds ONLY!
-# We use packaging/snap/snapcraft.yaml for beta and release builds
-# Check out build_snap.sh for more details
-name: adguard-home
-base: core18
-version: 'edge'
-summary: Network-wide ads & trackers blocking DNS server
-description: |
-  AdGuard Home is a network-wide software for blocking ads & tracking. After
-  you set it up, it'll cover ALL your home devices, and you don't need any
-  client-side software for that.
-
-  It operates as a DNS server that re-routes tracking domains to a "black hole,"
-  thus preventing your devices from connecting to those servers. It's based
-  on software we use for our public AdGuard DNS servers -- both share a lot
-  of common code.
-grade: stable
-confinement: strict
-
-architectures:
-  - build-on: amd64
-  - build-on: armhf
-  - build-on: i386
-  - build-on: arm64
-
-parts:
-  adguard-home:
-    plugin: make
-    source: .
-    build-snaps: [ node/13/stable, go ]
-    build-packages: [ git, build-essential ]
-    override-build: |
-      make clean
-      make
-      cp AdGuardHome ${SNAPCRAFT_PART_INSTALL}/
-apps:
-  adguard-home:
-    command: AdGuardHome -w ${SNAP_DATA} --no-check-update
-    plugs: [ network-bind ]
-    daemon: simple
-    restart-condition: always
diff --git a/tools.go b/tools.go
new file mode 100644
index 00000000..350f00a6
--- /dev/null
+++ b/tools.go
@@ -0,0 +1,8 @@
+// +build tools
+
+package tools
+
+import (
+	// Import packr
+	_ "github.com/gobuffalo/packr/packr"
+)