diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6ca28d7b4b44ae8230d39ef812478161985bee46
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,76 @@
+# NOTE #1: Our GitLab CI runs jobs inside Alpine Linux container by default.
+#
+# NOTE #2: Keep in mind that each job (e.g. test-gcc) is executed in a separate
+# isolated environment (it may be even on different machine). There are no
+# files implicitly passed between jobs or stages, only artifacts and maybe
+# cache (depends on configuration).
+
+before_script:
+  - case "$CI_BUILD_NAME" in
+      *-clang) export CXX=clang++ EXTRA_PKGS="$EXTRA_PKGS clang";;
+      *) export CXX=g++;;
+    esac
+  - export JOBS=$(grep -c processor /proc/cpuinfo)
+  # XXX: Remove after Alpine v3.6 is released and rename tclap-dev@edge to tclap-dev.
+  - echo '@edge http://repository.fit.cvut.cz/mirrors/alpine/edge/community' >> /etc/apk/repositories
+  - apk add --no-cache build-base bash libexecinfo-dev
+        cppunit-dev libxml2-dev tclap-dev@edge $EXTRA_PKGS
+
+
+#==========  Stage build  ==========
+
+.build: &build
+  script:
+    - make build-release
+  artifacts:
+    paths:
+      - bin-release/
+    expire_in: 6 hours
+  stage: build
+
+build-gcc:
+  <<: *build
+
+build-clang:
+  <<: *build
+
+build-doc:
+  variables:
+    EXTRA_PKGS: doxygen graphviz
+  script:
+    - make doc
+  allow_failure: true
+  artifacts:
+    name: docs
+    paths:
+      - alib2std/doc/
+      - alib2data/doc/
+      - alib2common/doc/
+      - alib2str/doc/
+      - alib2raw/doc/
+      - alib2algo/doc/
+      - alib2elgo/doc/
+      - alib2measurepp/doc/
+    expire_in: 1 week
+  stage: build
+
+
+#==========  Stage test  ==========
+
+.test: &test
+  variables:
+    # coreutils are needed because of timeout command used in tests.
+    EXTRA_PKGS: bc coreutils python3
+  script:
+    - make test-release
+  stage: test
+
+test-gcc:
+  <<: *test
+  dependencies:
+    - build-gcc
+
+test-clang:
+  <<: *test
+  dependencies:
+    - build-clang
diff --git a/makefile b/makefile
index 3ffc8694cde897987eaeb0130c685af58c5c5838..75740c53b442cc20f9c2ea44950774748efbe8ff 100644
--- a/makefile
+++ b/makefile
@@ -89,9 +89,9 @@ SUBDIRS_WITH_MAKE = $(dir $(wildcard */makefile))
 all:
 	@echo "What to do master?"
 
-debug: test-debug
+debug: | build-test-debug test-debug
 
-release: test-release
+release: | build-test-release test-release
 
 build-debug build-test-debug:
 	for dir in $(SUBDIRS_LIBS); do \
@@ -143,12 +143,12 @@ build-release build-test-release:
 	cp translateAddresses $(addsuffix -release, $(BINFOLDER)); \
 	cp xmlFormat $(addsuffix -release, $(BINFOLDER))
 
-test-debug: build-test-debug
+test-debug:
 	for test in $(wildcard tests.*.sh); do \
 		./$$test debug $(JOBS); \
 	done
 
-test-release: build-test-release
+test-release:
 	for test in $(wildcard tests.*.sh); do \
 		./$$test release $(JOBS); \
 	done