GitLab CI/CD Vue.js Integration and Deployment Architecture

The intersection of Vue.js development and GitLab CI/CD represents a sophisticated shift toward automated software delivery, allowing developers to move from local code commits to live production environments with minimal manual intervention. By leveraging GitLab's integrated pipeline capabilities, Vue.js applications can be subjected to rigorous build processes, automated testing, and diverse deployment strategies, ranging from static hosting via GitLab Pages to containerized orchestration within Kubernetes clusters. The core of this automation resides in the .gitlab-ci.yml file, which serves as the blueprint for the GitLab Runner, defining the exact sequence of stages—build, test, and deploy—that a codebase must traverse. This orchestration ensures that every push to a repository is validated, reducing the risk of regression and ensuring that the final output is optimized for the target environment.

Vue.js Project Initialization and Scaffolding

Before integrating with a CI/CD pipeline, a Vue.js application must be properly scaffolded. This process establishes the directory structure and dependency manifests required for the application to be buildable in a headless environment.

To begin the process, the Vue CLI must be installed globally on the development machine. This can be achieved using either npm or yarn:

npm install -g @vue/cli

Alternatively:

yarn global add @vue/cli

Once the installation is complete, verifying the correct version of Vue is essential to ensure compatibility with the intended build tools:

vue --version

The application is then generated using the create command:

vue create name-of-app

Executing this command creates a scaffolding of the VueJS application, providing the baseline structure—including the source folder, public assets, and configuration files—that will eventually be processed by the GitLab CI pipeline.

GitLab Pages Deployment Framework

GitLab Pages offers a streamlined, free hosting solution for Vue.js websites, utilizing the GitLab CI/CD pipeline to automate the deployment of static assets. This approach is particularly effective for documentation, landing pages, or small-scale web applications.

The fundamental requirement for GitLab Pages is that the deployment must originate from a specific folder named public. By default, Vue.js build processes output artifacts into a folder named dist. To bridge this gap, the pipeline configuration and the Vue configuration must be aligned to ensure the public directory is populated with the final build.

The following .gitlab-ci.yml configuration is required at the project root to facilitate this deployment:

yaml image: "node:16-alpine" stages: - build - test - deploy build: stage: build script: - yarn install --frozen-lockfile --check-files --non-interactive - yarn build artifacts: paths: - public pages: stage: deploy script: - echo 'Pages deployment job' artifacts: paths: - public only: - main

This configuration employs a node:16-alpine image, providing a lightweight environment that includes the necessary Node.js runtime. The pipeline is divided into three stages: build, test, and deploy. During the build stage, yarn install is executed with flags such as --frozen-lockfile and --non-interactive to ensure the environment is consistent and does not prompt for user input. The yarn build command then generates the production assets.

The critical adjustment occurs in the artifacts section, where the public folder is specified. Because GitLab Pages specifically looks for the public directory, the Vue configuration must be modified to change the output destination.

Vue Configuration for GitLab Pages

To ensure that the Vue.js build output aligns with the requirements of GitLab Pages, the vue.config.js file must be modified. This allows the developer to redefine the output directory and handle the base URL for the deployed application.

The vue.config.js should be configured as follows:

javascript const { defineConfig } = require('@vue/cli-service') function publicPath () { if (process.env.CI_PAGES_URL) { return new URL(process.env.CI_PAGES_URL).pathname } else { return '/' } } module.exports = defineConfig({ transpileDependencies: true, publicPath: publicPath(), outputDir: 'public' })

The outputDir property is set to public, which overrides the default dist folder. This ensure that GitLab can pick up the build artifacts and deploy them to the Pages service.

Furthermore, the publicPath is handled via a helper function. This function checks for the existence of the CI_PAGES_URL environment variable. If it exists, it returns the pathname of that URL; otherwise, it defaults to /. This is vital because the base URL of a GitLab Pages site varies depending on the project's namespace and page settings. Without this dynamic path, the application may fail to load assets correctly in the production environment.

Environmental Variable Integration in Vue.js

A common challenge in CI/CD pipelines is the need to pass dynamic data—such as pipeline IDs or application names—from the .gitlab-ci.yml variables into the Vue.js application code.

Handling Build Pipeline Variables

When a variable is defined in the .gitlab-ci.yml file, such as APP_NAME="My First Vue.js Application", it is not automatically available in the client-side Vue.js code. Because Vue is compiled into static JavaScript, it cannot access the shell environment of the GitLab Runner during runtime.

To resolve this, variables must be injected during the build process. One strategy is to write the variable into a file during the pipeline execution, which the application then references upon running.

Vite-Specific Variable Substitution

In projects utilizing Vite, there is a security layer that prevents the accidental leakage of sensitive environment data. Only variables that begin with the prefix VITE_ are substituted during the build process.

If a developer attempts to use a variable like VUE_APP_VERSION: "$CI_PIPELINE_ID", it will result in an undefined value when accessed via import.meta.env.VUE_APP_VERSION. To correctly implement this, the variable must be declared with the correct prefix:

yaml variables: VITE_APP_VERSION: $CI_PIPELINE_ID

With this declaration, the value of the pipeline ID is replaced by Vite during the build, allowing the application to access it via import.meta.env.VITE_APP_VERSION.

An alternative method involves the creation of a .env.local file within the environment directory during the compile stage. This is achieved by using an echo command in the script section of the CI configuration:

yaml compile: stage: build script: - echo -e "VITE_APP_VERSION=${CI_PIPELINE_ID}"

Containerization and Kubernetes Deployment

For larger-scale deployments, such as those utilizing a micro-service architecture, containerization via Docker is the preferred method. This ensures the application runs in a secure, consistent environment regardless of whether it is in development or production.

Multi-Stage Docker Build Process

A multi-stage Dockerfile is used to optimize the final image size, ensuring that only the necessary production artifacts are included, while build-time dependencies are discarded.

The following Dockerfile structure is employed:

```dockerfile

Build the Vue.js application

FROM node:current-alpine AS build
COPY . ./app
WORKDIR /app
RUN npm install
RUN npm run build

Final Nginx container

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
```

The process is broken down into two primary stages:

  1. The Build Stage: This stage uses a node:current-alpine image. It copies the source code into the /app directory, installs dependencies, and executes npm run build. This stage is computationally heavy and requires the full Node.js toolchain.
  2. The Final Stage: This stage uses an nginx:alpine image. It utilizes the --from=build flag to copy only the dist folder (the result of the build) into /usr/share/nginx/html. This results in a lightweight image that only contains the compiled assets and the Nginx server required to serve them.

GitLab CI/CD Pipeline for Docker

To deploy this containerized application to Kubernetes, the .gitlab-ci.yml must be configured to build the image and push it to the GitLab registry. This typically involves the use of the docker-in-docker (DinD) service.

The workflow involves:
- Building the Vue 3 application.
- Building the Docker image using the multi-stage Dockerfile.
- Pushing the image to the project's GitLab registry.

This architecture allows the Vue 3 project to serve as the UI layer, while the backend is handled separately as a REST API built with NestJS, each containerized and managed within a Kubernetes cluster.

Comparison of Deployment Strategies

The choice between GitLab Pages and Kubernetes depends on the scale and requirements of the application.

Feature GitLab Pages Kubernetes (Containerized)
Target Architecture Static Site / SPA Micro-services / Enterprise App
Host Environment GitLab Managed Kubernetes Cluster
Server Requirement None (Static) Nginx / Custom Web Server
Deployment Tool GitLab CI/CD (Pages) Docker + GitLab Registry + K8s
Artifact Path /public /dist (copied to Nginx)
Complexity Low High
Cost Free for personal projects Depends on Infrastructure

Analysis of CI/CD Efficiency

The integration of Vue.js with GitLab CI/CD fundamentally alters the development lifecycle by eliminating the "works on my machine" syndrome. The use of node:16-alpine or node:current-alpine ensures that the build environment is immutable and reproducible.

The implementation of yarn install --frozen-lockfile is a critical detail; it prevents the pipeline from updating dependencies unexpectedly, which would otherwise lead to non-deterministic builds and potential production crashes. Similarly, the move toward multi-stage Docker builds reduces the attack surface of the production image by removing the Node.js package manager and source code, leaving only the compiled assets and a hardened Nginx server.

From a configuration perspective, the dynamic handling of publicPath in vue.config.js solves the common issue of broken asset links in GitLab Pages. By leveraging CI_PAGES_URL, the application becomes portable across different GitLab namespaces. Furthermore, the transition from VUE_APP_ to VITE_ prefixes highlights the shift in the Vue ecosystem toward Vite, emphasizing the importance of environment-specific variable substitution to protect sensitive data.

Ultimately, the progression from simple static hosting to complex Kubernetes orchestration demonstrates the scalability of GitLab CI/CD. Whether deploying a simple site via GitLab Pages or a containerized UI for a NestJS backend, the core principle remains the same: defining a rigorous, automated pipeline that transforms raw source code into a production-ready asset.

Sources

  1. Hosting VueJS apps using GitLab Pages
  2. Vue.js app GitLab
  3. Using build variable in a Vue.js project
  4. Using CI pipeline ID value in my Vue.js app
  5. Containerize Vue 3 application with GitLab CI/CD

Related Posts