diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000000000000000000000000000000000000..4832d787d92fad663ef201dad1a64082abbd6289
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,6 @@
+.github/
+.gitlab/
+.idea/
+.gitlab-ci.yml
+LICENSE.txt
+readme.md
diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml
new file mode 100644
index 0000000000000000000000000000000000000000..aaf407311910c0ba016005098199400718d7ff9b
--- /dev/null
+++ b/.github/workflows/docker-build.yml
@@ -0,0 +1,28 @@
+---
+name: Build to Docker Hub
+
+on:
+  push:
+    branches: [master]
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout the repository
+        uses: actions/checkout@v4
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v3
+      - name: Login to Docker Hub
+        uses: docker/login-action@v3
+        with:
+          username: ${{ secrets.DOCKERHUB_USERNAME }}
+          password: ${{ secrets.DOCKERHUB_TOKEN }}
+      - name: Set up JDK 17
+        uses: actions/setup-java@v3
+        with:
+          java-version: '17'
+          distribution: 'temurin'
+          cache: maven
+      - name: Build & publish the project
+        run: mvn compile jib:build -X -DjibSerialize=true
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 37d0199ee01c08b586ed61fecf9487dee12aa5cb..40914893a4787d6e60f2bf3788d11c4f4de6c4a1 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -5,25 +5,17 @@ variables:
   # `showDateTime` will show the passed time in milliseconds. You need to specify `--batch-mode` to make this work.
   MAVEN_OPTS: >
     -Dhttps.protocols=TLSv1.2
-    -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository
+    -Dmaven.repo.local=${CI_PROJECT_DIR}/.m2/repository
     -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN
     -Dorg.slf4j.simpleLogger.showDateTime=true
     -Djava.awt.headless=true
-  # As of Maven 3.3.0 instead of this, you may define these options in `.mvn/maven.config` so the same config is used
-  # when running from the command line.
-  # `installAtEnd` and `deployAtEnd` are only effective with the recent version of the corresponding plugins.
-  MAVEN_CLI_OPTS: >
-    --batch-mode
-    --errors
-    --fail-at-end
-    --show-version
-    -DinstallAtEnd=true
-    -DdeployAtEnd=true
 
 # Check the list of available templates at https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates
 include:
   - template: Jobs/Secret-Detection.gitlab-ci.yml
+  - template: Jobs/Dependency-Scanning.latest.gitlab-ci.yml
   - template: Workflows/MergeRequest-Pipelines.gitlab-ci.yml
+  - local: .gitlab/ci/deploy-template.gitlab-ci.yml
   - local: .gitlab/ci/deploy-parallel-matrix.gitlab-ci.yml
 
 stages:
@@ -48,7 +40,7 @@ build:
     - pwd
   script:
     - mvn $MAVEN_CLI_OPTS -DskipTests
-      -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository package
+      -Dmaven.repo.local=${CI_PROJECT_DIR}/.m2/repository package
   cache:
     key:
       files:
@@ -88,27 +80,32 @@ test:
         - target/surefire-reports/TEST-*.xml
 
 deploy:
-  stage: deploy
-  image: docker.io/dokku/ci-docker-image:0.9.3
+  extends: .base-dokku-deploy
   rules: !reference [.upstream-deploy-production-rules, rules]
   variables:
-    APP_NAME: spring-petclinic-rest
-    GIT_DEPTH: "0"
-    SSH_REMOTE: "ssh://dokku@dokku-ppl.cs.ui.ac.id:22"
-    GIT_REMOTE_URL: "$SSH_REMOTE/$APP_NAME"
-    SSH_PRIVATE_KEY: $PRODUCTION_SSH_PRIVATE_KEY
-    BRANCH: $CI_DEFAULT_BRANCH
+    DOKKU_APP_NAME: spring-petclinic-rest
+    DOKKU_SSH_REMOTE: "ssh://dokku@dokku-ppl.cs.ui.ac.id:22"
+    DOKKU_SSH_PRIVATE_KEY: $PRODUCTION_SSH_PRIVATE_KEY
   script:
+    - dokku network:exists "${APP_NAME}-network"
+      || dokku network:create "${APP_NAME}-network"
+    - dokku postgres:exists "${APP_NAME}-database"
+      || dokku postgres:create "${APP_NAME}-database"
+         --image docker.io/library/postgres
+         --image-version 16.0-alpine
+         --post-create-network "${APP_NAME}-network"
+    - dokku apps:exists "$APP_NAME"
+      || dokku apps:create "$APP_NAME"
+    - dokku network:set "$APP_NAME" attach-post-create "${APP_NAME}-network"
+    - dokku postgres:link "${APP_NAME}-database" "$APP_NAME"
+    - dokku config:set "$APP_NAME" SPRING_PROFILES_ACTIVE="default,csui"
     - dokku-deploy
-    - sh .gitlab/dokku-ci-post-deploy.sh
-  after_script:
-    - dokku-unlock
-  environment:
-    name: production
-    url: "https://$APP_NAME.dokku-ppl.cs.ui.ac.id"
+    - sh .gitlab/bin/dokku-ci-post-deploy.sh
   needs:
     - test
-  dependencies: []
+  environment:
+    name: production
+    url: "https://${APP_NAME}.dokku-ppl.cs.ui.ac.id"
 
 visualize-coverage:
   stage: report
@@ -136,6 +133,6 @@ sonarqube-check:
   script:
     - mvn -DskipTests verify sonar:sonar
   cache:
-    key: "${CI_JOB_NAME}"
+    key: "$CI_JOB_NAME"
     paths:
       - .sonar/cache
diff --git a/.gitlab/bin/dokku-ci-post-deploy.sh b/.gitlab/bin/dokku-ci-post-deploy.sh
new file mode 100644
index 0000000000000000000000000000000000000000..91e90a8a148ee22501f55cb4281db4ebf96b0cd8
--- /dev/null
+++ b/.gitlab/bin/dokku-ci-post-deploy.sh
@@ -0,0 +1,31 @@
+#!/bin/sh -l
+set -e
+
+CERT_PATH="${CERT_PATH:-/home/dokku/.local/share/certs/cert.pem}"
+PRIVKEY_PATH="${PRIVKEY_PATH:-/home/dokku/.local/share/certs/privkey.pem}"
+
+if [ -z "$APP_NAME" ]; then
+  echo "Error: APP_NAME is not set. Ensure the script is executed in the dokku/ci-docker-image environment." >&2
+  exit 1
+fi
+
+if [ -z "$SSH_REMOTE" ]; then
+  echo "Error: SSH_REMOTE is not set. Ensure the script is executed in the dokku/ci-docker-image environment." >&2
+  exit 1
+fi
+
+dokku_cert_add() {
+  echo "⚙ Assigning certs at $CERT_PATH and $PRIVKEY_PATH to $APP_NAME ..."
+  ssh "$SSH_REMOTE" -- certs:add "$APP_NAME" "$CERT_PATH" "$PRIVKEY_PATH" || echo "Failed to assign certs" >&2
+}
+
+dokku_map_port_to_https() {
+  port="$1"
+  echo "⚙ Mapping $APP_NAME container port $port to HTTPS ..."
+  ssh "$SSH_REMOTE" -- ports:add "$APP_NAME" "https:443:$port" || echo "Failed to map container port $port to HTTPS" >&2
+}
+
+dokku_cert_add
+dokku_map_port_to_https 9966
+
+echo "✅ Post-deployment configuration completed"
diff --git a/.gitlab/bin/dokku.sh b/.gitlab/bin/dokku.sh
new file mode 100755
index 0000000000000000000000000000000000000000..722ce87258c549219caa8823f4a78d90798d1cf9
--- /dev/null
+++ b/.gitlab/bin/dokku.sh
@@ -0,0 +1,31 @@
+#!/bin/sh -l
+# Dokku wrapper script, based from https://github.com/dokku/ci-docker-image/issues/24#issuecomment-1211264189
+# by @mcgaw on GitHub
+set -e
+
+check_command() {
+  if ! command -v "$1" >/dev/null 2>&1; then
+    echo "Error: '$1' not found. Ensure the script is executed in the dokku/ci-docker-image environment." >&2
+    exit 1
+  fi
+}
+
+check_command setup-ssh
+check_command parse-ci-commit
+check_command parse-app-name
+check_command parse-ssh-host
+check_command parse-ssh-port
+check_command ssh
+
+setup-ssh
+
+commit_sha="$(parse-ci-commit)"
+app_name="$(parse-app-name)"
+
+if [ -z "$SSH_REMOTE" ]; then
+  ssh_remote="ssh://dokku@$(parse-ssh-host):$(parse-ssh-port)"
+else
+  ssh_remote="$SSH_REMOTE"
+fi
+
+ssh "$ssh_remote" -- "#@"
diff --git a/.gitlab/ci/deploy-parallel-matrix.gitlab-ci.yml b/.gitlab/ci/deploy-parallel-matrix.gitlab-ci.yml
index ab3b799d4ab29cf7edef3ae489e1c2dd12a56ec2..8577e0970e65382b49b3d07d89ff2c0fc84776d3 100644
--- a/.gitlab/ci/deploy-parallel-matrix.gitlab-ci.yml
+++ b/.gitlab/ci/deploy-parallel-matrix.gitlab-ci.yml
@@ -1,20 +1,5 @@
 ---
-deploy-workshop:
-  stage: deploy
-  image: docker.io/dokku/ci-docker-image:0.9.3
-  rules: !reference [.upstream-deploy-production-rules, rules]
-  variables:
-    APP_NAME: "spring-petclinic-rest-$PARTICIPANT_NAME"
-    GIT_DEPTH: "0"
-    SSH_REMOTE: "ssh://dokku@dokku-ppl.cs.ui.ac.id:22"
-    GIT_REMOTE_URL: "$SSH_REMOTE/$APP_NAME"
-    SSH_PRIVATE_KEY: $WORKSHOP_SSH_PRIVATE_KEY
-    BRANCH: $CI_DEFAULT_BRANCH
-  script:
-    - dokku-deploy
-    - sh .gitlab/dokku-ci-post-deploy.sh
-  after_script:
-    - dokku-unlock
+.participants: &parallel-by-participants
   parallel:
     matrix:
       - PARTICIPANT_NAME:
@@ -35,9 +20,21 @@ deploy-workshop:
           - sonya
           - kamrozi
           - agra
+
+deploy-workshop:
+  extends: .base-dokku-deploy
+  rules: !reference [ .upstream-deploy-production-rules, rules ]
+  variables:
+    DOKKU_APP_NAME: "spring-petclinic-rest-$PARTICIPANT_NAME"
+    DOKKU_SSH_REMOTE: "ssh://dokku@dokku-ppl.cs.ui.ac.id:22"
+    DOKKU_SSH_PRIVATE_KEY: $WORKSHOP_SSH_PRIVATE_KEY
+  <<: *parallel-by-participants
+  script:
+    - dokku-deploy
+    - sh .gitlab/dokku-ci-post-deploy.sh
+  needs:
+    - test
   environment:
     name: workshop/$PARTICIPANT_NAME
     url: "https://$APP_NAME.dokku-ppl.cs.ui.ac.id"
-  needs:
-    - test
-  dependencies: []
+  resource_group: workshop
diff --git a/.gitlab/ci/deploy-template.gitlab-ci.yml b/.gitlab/ci/deploy-template.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f59611eec745efe2f50b903c1199b9b4de848c3e
--- /dev/null
+++ b/.gitlab/ci/deploy-template.gitlab-ci.yml
@@ -0,0 +1,15 @@
+---
+.base-dokku-deploy:
+  stage: deploy
+  image: docker.io/dokku/ci-docker-image:0.9.3
+  variables:
+    APP_NAME: "${DOKKU_APP_NAME:-$CI_PROJECT_PATH_SLUG}"
+    SSH_REMOTE: "${DOKKU_SSH_REMOTE:-ssh://dokku@dokku-ppl.cs.ui.ac.id:22}"
+    SSH_PRIVATE_KEY: "${DOKKU_SSH_PRIVATE_KEY:-}"
+    BRANCH: $CI_DEFAULT_BRANCH
+    GIT_DEPTH: 0
+  before_script: >
+    [ -f .gitlab/bin/dokku.sh ] \
+    && chmod +x .gitlab/bin/dokku.sh \
+    && cp .gitlab/bin/dokku.sh /bin/dokku
+  dependencies: []
diff --git a/.gitlab/dokku-ci-post-deploy.sh b/.gitlab/dokku-ci-post-deploy.sh
deleted file mode 100755
index 2b776b01996b06a08f573b586d46428decab8973..0000000000000000000000000000000000000000
--- a/.gitlab/dokku-ci-post-deploy.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/sh -l
-export CERT_PATH="/home/dokku/.local/share/certs/cert.pem"
-export PRIVKEY_PATH="/home/dokku/.local/share/certs/privkey.pem"
-
-echo "⚙ Assigning certs at $CERT_PATH and $PRIVKEY_PATH to $APP_NAME ..."
-ssh "$SSH_REMOTE" -- certs:add "$APP_NAME" "$CERT_PATH" "$PRIVKEY_PATH" || echo "Failed to assign certs"
-
-echo "⚙ Mapping $APP_NAME container port 9966 to HTTPS ..."
-ssh "$SSH_REMOTE" -- ports:add "$APP_NAME" https:443:9966 || echo "Failed to map container port to HTTPS"
-
-echo "✅ Post-deploy configuration completed"
diff --git a/Dockerfile b/Dockerfile
index 148295997e900da32b6d96051b059c345787636d..024acc0177c738e0b659ecf4632845224f854f6e 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
 # Use JDK & Maven image to build the application
-FROM docker.io/library/maven:3.9.4-eclipse-temurin-17-alpine AS builder
+FROM docker.io/library/maven:3.9.5-eclipse-temurin-17-alpine AS builder
 
 # Set the working directory inside the container
 WORKDIR /src
@@ -11,7 +11,7 @@ COPY . .
 RUN mvn -DskipTests package
 
 # Use JRE image for running the application
-FROM docker.io/library/eclipse-temurin:17.0.8.1_1-jre-alpine
+FROM docker.io/library/eclipse-temurin:17.0.9_9-jre-alpine
 
 # Create a non-root user named "app" to own and run the application
 RUN addgroup app \
@@ -29,5 +29,12 @@ COPY --chown=app:app --from=builder /src/target/spring-petclinic-rest-*.jar .
 # Expose port 9966
 EXPOSE 9966
 
+# Configure Spring Boot application profile via environment variable
+ENV SPRING_PROFILES_ACTIVE="default"
+
 # Run the application JAR file
 CMD ["/bin/sh", "-c", "java -jar spring-petclinic-rest-*.jar"]
+
+# Add container healthcheck
+HEALTHCHECK CMD curl -f http://localhost:9966/petclinic/actuator/health \
+    | grep --quiet '"status":"UP"' || exit 1
diff --git a/readme.md b/readme.md
index 790ed840676ef7480dd64bb65bff03e8fa7913c4..0499c6eb08e17721309e5432c322013d997cda98 100644
--- a/readme.md
+++ b/readme.md
@@ -1,6 +1,7 @@
 # REST version of Spring PetClinic Sample Application (spring-framework-petclinic extend ) 
 
-[![Build Status](https://github.com/spring-petclinic/spring-petclinic-rest/actions/workflows/maven-build.yml/badge.svg)](https://github.com/spring-petclinic/spring-petclinic-rest/actions/workflows/maven-build.yml)
+[![pipeline status](https://gitlab.cs.ui.ac.id/pmpl/examples/spring-petclinic-rest/badges/csui/pipeline.svg)](https://gitlab.cs.ui.ac.id/pmpl/examples/spring-petclinic-rest/-/commits/csui)
+[![coverage report](https://gitlab.cs.ui.ac.id/pmpl/examples/spring-petclinic-rest/badges/csui/coverage.svg)](https://gitlab.cs.ui.ac.id/pmpl/examples/spring-petclinic-rest/-/commits/csui)
 
 This backend version of the Spring Petclinic application only provides a REST API. **There is no UI**.
 The [spring-petclinic-angular project](https://github.com/spring-petclinic/spring-petclinic-angular) is a Angular front-end application which consumes the REST API.
@@ -16,6 +17,7 @@ The [spring-petclinic-angular project](https://github.com/spring-petclinic/sprin
 ## Running petclinic locally
 
 ### With maven command line
+
 ```
 git clone https://github.com/spring-petclinic/spring-petclinic-rest.git
 cd spring-petclinic-rest
@@ -23,6 +25,7 @@ cd spring-petclinic-rest
 ```
 
 ### With Docker
+
 ```
 docker run -p 9966:9966 springcommunity/spring-petclinic-rest
 ```
@@ -136,6 +139,7 @@ mvn clean install
 ```
 
 ## Security configuration
+
 In its default configuration, Petclinic doesn't have authentication and authorization enabled.
 
 ### Basic Authentication
@@ -187,7 +191,6 @@ git clone https://github.com/spring-petclinic/spring-petclinic-rest.git
 File -> Import -> Maven -> Existing Maven project
 ```
 
-
 ## Looking for something in particular?
 
 | Layer | Source |
@@ -222,13 +225,8 @@ hosted in a special GitHub org: [spring-petclinic](https://github.com/spring-pet
 If you have a special interest in a different technology stack
 that could be used to implement the Pet Clinic then please join the community there.
 
-
 # Contributing
 
 The [issue tracker](https://github.com/spring-petclinic/spring-petclinic-rest/issues) is the preferred channel for bug reports, features requests and submitting pull requests.
 
 For pull requests, editor preferences are available in the [editor config](https://github.com/spring-petclinic/spring-petclinic-rest/blob/master/.editorconfig) for easy use in common text editors. Read more and download plugins at <http://editorconfig.org>.
-
-
-
-
diff --git a/src/main/resources/application-csui.properties b/src/main/resources/application-csui.properties
new file mode 100644
index 0000000000000000000000000000000000000000..353f5756f8294c9010c6122ad4c9bea891846a01
--- /dev/null
+++ b/src/main/resources/application-csui.properties
@@ -0,0 +1,17 @@
+# Application configuration
+petclinic.security.enable=true
+
+# Database configuration
+spring.sql.init.mode=always
+spring.sql.init.schema-locations=classpath*:db/postgresql/initDB.sql
+spring.sql.init.data-locations=classpath*:db/postgresql/populateDB.sql
+
+# PostgreSQL config start
+#----------------------------------------------------------------
+spring.datasource.url=${DATABASE_URL:jdbc:postgresql://localhost:5432/petclinic}
+spring.datasource.driver-class-name=org.postgresql.Driver
+spring.jpa.database=POSTGRESQL
+spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
+spring.jpa.hibernate.ddl-auto=none
+#----------------------------------------------------------------
+# PostgreSQL config end