Deploying Laravel & Vue with GitLab CI/CD to Shared Hosting

Deploying Laravel & Vue with GitLab CI/CD to Shared Hosting

Laravel
Vue.js
GitLab
CI/CD
Deployment
cPanel

Deploying a modern web application, especially one with a separate frontend and backend like Laravel and Vue.js, can be a complex process. While setting up a GitLab CI/CD pipeline promises automation, it often leads to a common, frustrating problem: the pipeline succeeds, but the live website shows a blank page with missing data and broken images.

Recently, while working on a project, I went through this exact scenario. The GitLab job log showed "Job succeeded," but the deployed site was non-functional. This led to a deep dive into server-side configurations, permissions, and application caches—issues that occur after the pipeline has successfully delivered the files.

In this post, I'll document the complete, battle-tested solution. We will build a secure GitLab CI/CD pipeline from scratch and, more importantly, cover the essential server-side commands required to bring your application to life post-deployment. This guide will serve as a definitive checklist for a successful, automated deployment.


The Goal: A Secure, "Hands-Off" Deployment

Our primary goal is to create a pipeline that automates the deployment of code and assets without ever touching the production .env file on the server. This principle is crucial for security. The application code lives in Git, but the server's secrets (database passwords, API keys) live only on the server.


1. Configure GitLab CI/CD Variables

This is the most critical step. All sensitive information must be stored as CI/CD variables in your GitLab project. Navigate to:

Settings > CI/CD → expand the Variables section.

Deployment & Server Credentials

KeyValueNotes
CPANEL_HOSTYour server's IP address or hostname
CPANEL_USERYour cPanel/SSH username
CPANEL_PASSYour SSH passwordMask as Masked & Protected
CPANEL_PATHAbsolute path to your app's root directory (e.g., /home/user/public_html)
SSH_PORT2223 (or your custom SSH port)

Frontend Build Variables

These variables are safely passed to the npm run prod command.

KeyValue
APP_URLhttps://test.dkstore.id
PUSHER_PORT443
PUSHER_SCHEMEhttps
PUSHER_APP_CLUSTERmt1
MIX_API_KEYYour API key (Masked)
DEMOfalse

2. The .gitlab-ci.yml Pipeline

This two-stage pipeline separates building assets from deploying the application. It creates a temporary, safe .env file for the frontend build and uses rsync for efficient file transfer.

stages:
  - build
  - deploy

# Stage 1: Build Frontend Assets
build_assets:
  stage: build
  image: node:18
  only:
    - staging # Or your deployment branch
  script:
    - echo "Installing frontend dependencies..."
    - npm install

    - echo "Creating temporary .env for frontend build..."
    - echo "APP_URL=${APP_URL}" > .env
    - echo "PUSHER_PORT=${PUSHER_PORT}" >> .env
    - echo "PUSHER_SCHEME=${PUSHER_SCHEME}" >> .env
    - echo "PUSHER_APP_CLUSTER=${PUSHER_APP_CLUSTER}" >> .env
    - echo "MIX_API_KEY=${MIX_API_KEY}" >> .env
    - echo "DEMO=${DEMO}" >> .env
    - echo "MIX_HOST=${APP_URL}" >> .env
    - echo "VITE_PUSHER_PORT=${PUSHER_PORT}" >> .env
    - echo "VITE_PUSHER_SCHEME=${PUSHER_SCHEME}" >> .env
    - echo "VITE_PUSHER_APP_CLUSTER=${PUSHER_APP_CLUSTER}" >> .env

    - echo "Building production assets..."
    - npm run prod

  artifacts:
    paths:
      - public/js/
      - public/css/
    expire_in: 1 hour
  cache:
    key: ${CI_COMMIT_REF_SLUG}
    paths:
      - node_modules/

# Stage 2: Deploy Application to Server
deploy_to_production:
  stage: deploy
  image: ubuntu:22.04
  before_script:
    - apt-get update -qy && apt-get install -y sshpass rsync
  script:
    - echo "Deploying application with rsync..."
    - sshpass -p "$CPANEL_PASS" rsync -avz -e "ssh -p $SSH_PORT -o StrictHostKeyChecking=no" \
      --exclude ".git*" \
      --exclude ".gitlab-ci.yml" \
      --exclude "node_modules/" \
      --exclude ".env" \
      . $CPANEL_USER@$CPANEL_HOST:$CPANEL_PATH/

    - echo "Running final commands on the server..."
    - sshpass -p "$CPANEL_PASS" ssh -p $SSH_PORT $CPANEL_USER@$CPANEL_HOST "cd $CPANEL_PATH && php artisan optimize:clear && php artisan storage:link"

  dependencies:
    - build_assets