Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

bwrigley's avatar

I think I have deployment all wrong...

I don't yet have a live production site, but as I have been developing my project, I have been incrementally deploying to a production environment while I'm getting ready.

My project is Laravel/Vue and I have been deploying this way:

  1. npm run build to compile my js/css assets
  2. push all changes to my git repo
  3. ssh to my shared hosting and pull from git repo
  4. run any composer and npm updates

I've only just realised that npm run build is compiling elements of my dev .env file in the assets and using these in production. It took me ages to understand why my production Vue components were looking at a different Algolia application to my production Laravel backend!

Can someone point me to the correct way to do this so that my Vue components use the parameters from my product .env? Do I need to build my assets in my production environment instead?

In case an example is needed. I have a composable I use for search which uses the algoliasearch/lite library and instantiates with:

  /** Create algolia client */
  const searchClient = algoliasearch(
    import.meta.env.VITE_ALGOLIA_APP_ID,
    import.meta.env.VITE_ALGOLIA_SEARCH
  );

but in production I hadn't yet added aliases for VITE_ALGOLIA_APP_ID and VITE_ALGOLIA_SEARCH and yet it was still working but using my development app credentials.

I searched my code base for the development Algolia ID and found it in an asset of a Vue component that uses the search composable above.

0 likes
8 replies
gych's avatar

I always build my application on the production environment and I also use a deploy script with a pipeline that handles the deployment. You push your changes to git, merge to the production branch and this will deploy your application to the server.

And I also always deploy to a staging environment first because if something doesn't work as expected on the server I can already catch it on the staging environment instead of it breaking the live site.

1 like
bwrigley's avatar

@gych Thanks for your reply! Yes it makes sense. I will also introduce a staging environment before going live for the first time. Thanks again!

nexxai's avatar

If you want to do "proper" (I use that term almost facetiously, since it doesn't really exist), what you would do is:

  1. Build your app locally
  2. Push the changes to something like Github
  3. Write a Github Action (or similar automation tool in your git host of choice) that takes a previously configured .env.production file and copies it to .env on the runner then compiles your assets
  4. In that same Github Action runner, have it SSH to your server, do php artisan down, push your code to your server, do any other cache clearing stuff, then php artisan up

If you're interested, I can share the Github Action and deploy script I use to do this (with one caveat: I use a different system called Ploi that does the on-server stuff, so you'd just have to integrate the two, but the steps are the same)

1 like
nexxai's avatar

@bwrigley Feel free to use as is or customize it to your liking.

GitHub Action:

name: Deployment

on:
  push:

  workflow_dispatch:

jobs:
  fabpot-security-checker:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout the repository
        uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2

      - name: Install security-checker
        uses: symfonycorp/security-checker-action@v5

  pest:
    runs-on: ubuntu-latest
    needs: [fabpot-security-checker, phpcpd]

    services:
      mariadb:
        image: mariadb:10.11
        env:
          MYSQL_ROOT_PASSWORD: root
        ports:
          - 3306:3306
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3

      redis:
        image: redis:7.0-alpine
        ports:
          - 6379:6379
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - name: Checkout the repository
        uses: actions/checkout@v4

      - name: Setup PHP with composer v2
        uses: shivammathur/setup-php@v2
        with:
          php-version: "8.2"
          tools: composer:v2
          extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, mysql, mysqli, pdo_mysql, bcmath, soap, intl, gd, exif, iconv, imagick, redis
          coverage: xdebug

      - name: Install composer packages
        run: |
          php -v
          composer install --prefer-dist --no-ansi --no-interaction --no-progress --no-scripts

      - name: npm install
        run: |
          npm --version
          npm install
          npm run production

      - name: Create temp database
        run: |
          mysql --host 127.0.0.1 -uroot -proot -e "CREATE DATABASE IF NOT EXISTS laravel_db"

      - name: Execute tests
        env:
          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        run: |
          cp .env.github .env
          php -v
          php artisan migrate --force
          php artisan key:generate
          ./vendor/bin/pest --parallel --coverage-clover=coverage.xml
          bash <(curl -s https://codecov.io/bash) || echo 'Codecov failed to upload'

  phpstan:
    runs-on: ubuntu-latest
    needs: [fabpot-security-checker, phpcpd]

    steps:
      - name: Checkout the repository
        uses: actions/checkout@v4

      - name: Setup PHP with composer v2
        uses: shivammathur/setup-php@v2
        with:
          php-version: "8.2"
          extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, mysql, mysqli, pdo_mysql, bcmath, soap, intl, gd, exif, iconv, imagick, redis
          tools: composer:v2

      - name: Install composer packages
        run: |
          php -v
          composer install --prefer-dist --no-ansi --no-interaction --no-progress --no-scripts

      - name: Static analysis with phpstan
        run: ./vendor/bin/phpstan analyse

  phpcpd:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout the repository
        uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2

      - name: Install phpcpd
        run: test -f phpcpd.phar || curl -L https://phar.phpunit.de/phpcpd.phar -o phpcpd.phar

      - name: Run phpcpd
        run: php phpcpd.phar app/ --min-lines=50

  ploi_deploy:
    runs-on: ubuntu-latest
    needs: [pest, phpstan]
    if: github.ref == 'refs/heads/main'

    steps:
      - name: Trigger Ploi webhook
        run: curl -X POST ${{ secrets.PLOI_DEPLOY_WEBHOOK }}

Ploi deployment script:

php artisan down --refresh=15 --render="errors::503" --with-secret

git reset --hard
git pull origin main
composer install --no-interaction --prefer-dist --optimize-autoloader --no-dev

npm ci
npm run build

echo "" | sudo -S service php8.2-fpm reload

php artisan config:cache
php artisan event:cache
php artisan route:cache
php artisan view:cache

{FLUSH_CLOUDFLARE_CACHE}

php artisan migrate --force
php artisan queue:restart
php artisan horizon:terminate # supervisord should pick this up and then restart it

php artisan up

echo "🚀 Application deployed!"
1 like
MohamedTammam's avatar

Run npm run build on the server not in your local.

I also recommend previous comments to create a pipeline or deployment script.

1 like

Please or to participate in this conversation.