diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..afe790e556146c34ec8327c6f17dee91a1dd9680
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,30 @@
+stages:
+  - build
+  - test_deploy
+
+image: docker:20.10.8
+
+variables:
+  DOCKER_TLS_CERTDIR: "/certs"
+  IMAGE_TAG_APP: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
+
+services:
+  - docker:20.10.8-dind
+
+before_script:
+  - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
+
+build_app:
+  stage: build
+  script:
+    - docker pull $CI_REGISTRY_IMAGE:test || true
+    - docker build --cache-from $CI_REGISTRY_IMAGE:test -t $IMAGE_TAG_APP .
+    - docker push $IMAGE_TAG_APP
+
+deploy:
+  stage: test_deploy
+  when: manual
+  before_script:
+    - apk add --update curl && rm -rf /var/cache/apk/*
+  script:
+    - curl -k -X POST $DEPLOY_HOOK
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..844065e0645794343700aef0325b5c6289ea3c77
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,34 @@
+FROM python:3.11
+
+RUN mkdir /app
+WORKDIR /app
+
+RUN curl -fsSL https://deb.nodesource.com/setup_19.x | bash -
+RUN apt-get install nodejs && rm -rf /var/lib/apt/lists/*
+
+COPY . .
+
+RUN pip install -r requirements/base.txt -r requirements/production.txt
+RUN npm install
+RUN npm run build
+
+# Placeholder values so the static files collect
+RUN DATABASE_URL=postgres://x/x \
+    SECRET_KEY=x \
+    ALLOWED_HOSTS=x \
+    SITE_URL=x \
+    OIDC_RP_REALM_URL=x \
+    OIDC_RP_CLIENT_ID=x \
+    OIDC_RP_CLIENT_SECRET=x \
+    python manage.py collectstatic --noinput --settings=ucebnice.settings.production
+
+RUN bash -c "adduser --disabled-login --quiet --gecos app app &&  \
+             chmod -R o+r /app/ && \
+             chmod o+x /app/run.sh"
+USER app
+
+ENV DJANGO_SETTINGS_MODULE "ucebnice.settings.production"
+
+EXPOSE 8000
+
+CMD ["bash", "run.sh"]