diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 6c67314d64eba52c79e514810a4864a6a591d3b2..633f20ce2ca0a165d1fa9f5554be0201926d66ab 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -12,14 +12,37 @@ variables:
   image: "registry.gitlab.haskell.org/ghc/ci-images/x86_64-linux-deb9:$DOCKER_REV"
   tags:
     - x86_64-linux
+  variables:
+    OS: "LINUX"
+
+.alpine:
+  image: "alpine:edge"
+  tags:
+    - x86_64-linux
+  variables:
+    OS: "LINUX"
 
 .darwin:
   tags:
     - x86_64-darwin
+  variables:
+    OS: "DARWIN"
 
 .freebsd:
   tags:
     - x86_64-freebsd
+  variables:
+    OS: "FREEBSD"
+
+.root_cleanup:
+  after_script:
+    - BUILD_DIR=$CI_PROJECT_DIR
+    - echo "Cleaning $BUILD_DIR"
+    - cd $HOME
+    - test -n "$BUILD_DIR"
+    - shopt -s extglob
+    - rm -Rf "$BUILD_DIR"/!(out)
+    - exit 0
 
 .test_ghcup_version:
   script:
@@ -33,41 +56,32 @@ variables:
     - .debian
   before_script:
     - ./.gitlab/before_script/linux/install_deps.sh
-  variables:
-    OS: "LINUX"
 
 .test_ghcup_version:darwin:
   extends:
     - .test_ghcup_version
     - .darwin
+    - .root_cleanup
   before_script:
     - ./.gitlab/before_script/darwin/install_deps.sh
-  variables:
-    OS: "DARWIN"
-  after_script:
-    - BUILD_DIR=$CI_PROJECT_DIR
-    - echo "Cleaning $BUILD_DIR"
-    - cd $HOME
-    - rm -Rf "$BUILD_DIR"/*
-    - exit 0
-
 
 .test_ghcup_version:freebsd:
   extends:
     - .test_ghcup_version
     - .freebsd
+    - .root_cleanup
   before_script:
     - ./.gitlab/before_script/freebsd/install_deps.sh
-  variables:
-    OS: "FREEBSD"
-  after_script:
-    - BUILD_DIR=$CI_PROJECT_DIR
-    - echo "Cleaning $BUILD_DIR"
-    - cd $HOME
-    - rm -Rf "$BUILD_DIR"/*
-    - exit 0
-
 
+.release_ghcup:
+  script:
+    - ./.gitlab/script/ghcup_release.sh
+  artifacts:
+    expire_in: 2 week
+    paths:
+      - out
+  only:
+    - tags
 
 ######## linux test ########
 
@@ -116,3 +130,46 @@ test:freebsd:latest:
     CABAL_VERSION: "3.2.0.0"
   allow_failure: true
 
+
+######## linux release ########
+
+release:linux:
+  extends:
+    - .alpine
+    - .release_ghcup
+  before_script:
+    - ./.gitlab/before_script/linux/alpine/install_deps.sh
+  variables:
+    ARTIFACT: "x86_64-linux-ghcup"
+    GHC_VERSION: "8.8.3"
+
+
+######## darwin release ########
+
+release:darwin:
+  extends:
+    - .darwin
+    - .release_ghcup
+    - .root_cleanup
+  before_script:
+    - ./.gitlab/before_script/darwin/install_deps.sh
+  variables:
+    ARTIFACT: "x86_64-apple-darwin-ghcup"
+    GHC_VERSION: "8.8.3"
+    CABAL_VERSION: "3.2.0.0"
+    MACOSX_DEPLOYMENT_TARGET: "10.7"
+
+
+######## freebsd release ########
+
+release:freebsd:
+  extends:
+    - .freebsd
+    - .release_ghcup
+    - .root_cleanup
+  before_script:
+    - ./.gitlab/before_script/freebsd/install_deps.sh
+  variables:
+    ARTIFACT: "x86_64-portbld-freebsd-ghcup"
+    GHC_VERSION: "8.6.5"
+
diff --git a/.gitlab/before_script/linux/alpine/install_deps.sh b/.gitlab/before_script/linux/alpine/install_deps.sh
new file mode 100755
index 0000000000000000000000000000000000000000..1c0db8510e63cea9673323e09e49cc50fc8c82ca
--- /dev/null
+++ b/.gitlab/before_script/linux/alpine/install_deps.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+set -eux
+
+apk add --no-cache \
+	curl \
+	gcc \
+	g++ \
+	gmp-dev \
+	ncurses-dev \
+	libffi-dev \
+	make \
+	xz \
+	tar \
+	perl \
+	\
+	cabal \
+	ghc
+
+# utils
+apk add --no-cache \
+	bash
+
+## Package specific
+apk add --no-cache \
+	zlib \
+	zlib-dev \
+	zlib-static \
+	gmp \
+	gmp-dev \
+	openssl-dev \
+	openssl-libs-static \
+	xz \
+	xz-dev
+
+
+. "$( cd "$(dirname "$0")" ; pwd -P )/../../../ghcup_env"
+
diff --git a/.gitlab/script/ghcup_release.sh b/.gitlab/script/ghcup_release.sh
new file mode 100755
index 0000000000000000000000000000000000000000..898c50f95c2ed38c65c7ae7115b8c9e567f2a150
--- /dev/null
+++ b/.gitlab/script/ghcup_release.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+set -eux
+
+. "$( cd "$(dirname "$0")" ; pwd -P )/../ghcup_env"
+
+mkdir -p "$CI_PROJECT_DIR"/.local/bin
+
+ecabal() {
+	cabal --store-dir="$(pwd)"/.store "$@"
+}
+
+# build
+ecabal update
+
+if [ "${OS}" = "LINUX" ] ; then
+	ecabal build -w ghc-${GHC_VERSION} -fcurl --ghc-options='-split-sections -optl-static'
+else
+	ecabal build -w ghc-${GHC_VERSION} -fcurl
+fi
+
+mkdir out
+cp "$(ecabal new-exec --verbose=0 --offline sh -- -c 'command -v ghcup')" .
+ver=$(./ghcup --numeric-version)
+cp ghcup out/${ARTIFACT}-${ver}
+