Skip to content

Deploy apps using GitHub Actions

Using GitHub Actions, you can automatically deploy your apps when you push or merge changes to your git repository.

The guide below shows how to deploy the following types of apps:

  • A Laravel app that uses MySQL.
  • A static site built with Astro.

To deploy other apps, use one of the GitHub Actions workflows below as a starting point for creating a workflow to deploy your app.

Create a deployment SSH key

Create an SSH key that GitHub Actions will use to SSH into your server when deploying the app.

  1. SSH into the server as the app’s system user.
  2. Create an SSH key pair without a password.
    Terminal window
    ssh-keygen -f ~/.ssh/github_actions -q -N ""
  3. Add the public key to the user’s .ssh/authorized_keys file.
    Terminal window
    umask 077 && mkdir -p ~/.ssh && touch ~/.ssh/authorized_keys
    cat ~/.ssh/github_actions.pub >> ~/.ssh/authorized_keys
  4. Display the private key. You’ll need the private key when configuring the GitHub repository’s environment.
    Terminal window
    cat ~/.ssh/github_actions

Configure a GitHub environment

The GitHub Actions workflow that does the deployment will use secrets and environment variables that are specific to the environment. In most cases, you will at least have a “prod” or “production” environment. You may also have other environments such as “dev” and “stage”.

  1. In GitHub, go to the repository’s Settings page.
  2. Click Environments.
  3. If the desired environment already exists, click the name of the environment.
  4. If the desired environment does not already exist:
    1. Click New environment.
    2. Enter a name for the environment (for example, “prod”).
    3. Click Configure environment.
  5. Click Add environment secret.
    1. For Name, enter SSH_PRIVATE_KEY.
    2. For Value, paste the private key.
    3. Click Add secret.
  6. Click Add environment variable.
    1. For Name, enter REMOTE_HOST.
    2. For Value, enter the server’s IP address.
    3. Click Add variable.
  7. Click Add environment variable.
    1. For Name, enter REMOTE_USER.
    2. For Value, enter the name of the app’s system user.
    3. Click Add variable.
  8. Click Add environment variable.
    1. For Name, enter APP_NAME.
    2. For Value, enter the app’s name.
    3. Click Add variable.

Next, add secrets and environment variables that are specific to the type of application you’re deploying.

  1. Click Add environment variable.
    1. For Name, enter DB_DATABASE.
    2. For Value, enter the app’s database name.
    3. Click Add variable.
  2. Click Add environment variable.
    1. For Name, enter DB_USERNAME.
    2. For Value, enter the app’s database username.
    3. Click Add variable.
  3. Click Add environment secret.
    1. For Name, enter DB_PASSWORD.
    2. For Value, enter the app’s database password.
    3. Click Add secret.
  4. Click Add environment secret.
    1. For Name, enter LARAVEL_APP_KEY.
    2. For Value, paste the value of the Laravel app’s APP_KEY. Include the base64: at the start of the value.
    3. Click Add secret.

Create a workflow

GitHub Actions workflows are YAML files you create in your git repo’s .github/workflows/ directory. The workflow defines the steps performed by GitHub Actions when you push code or merge a PR to your default branch.

  1. In your git repo, create the file .github/workflows/deploy.yaml with the following contents.
    .github/workflows/deploy.yaml
    name: Deploy Laravel
    on:
    # Run the workflow on pushes to the default branch.
    push:
    branches: [$default-branch]
    # Allows running this workflow manually from the Actions tab.
    workflow_dispatch:
    # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
    concurrency:
    group: ${{ github.workflow }}-${{ github.ref }}
    cancel-in-progress: false
    jobs:
    deploy:
    name: Deploy
    runs-on: ubuntu-latest
    environment:
    name: prod
    steps:
    - name: Checkout
    uses: actions/checkout@v4
    - name: Create .env file
    run: |
    cat <<-END_OF_ENV >>.env
    APP_NAME=Laravel
    APP_ENV=local
    APP_KEY=${{ secrets.LARAVEL_APP_KEY }}
    APP_DEBUG=true
    APP_URL=http://localhost
    APP_LOCALE=en
    APP_FALLBACK_LOCALE=en
    APP_FAKER_LOCALE=en_US
    APP_MAINTENANCE_DRIVER=file
    # APP_MAINTENANCE_STORE=database
    PHP_CLI_SERVER_WORKERS=4
    BCRYPT_ROUNDS=12
    LOG_CHANNEL=stack
    LOG_STACK=single
    LOG_DEPRECATIONS_CHANNEL=null
    LOG_LEVEL=debug
    DB_CONNECTION=mysql
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=${{ vars.DB_DATABASE }}
    DB_USERNAME=${{ vars.DB_USERNAME }}
    DB_PASSWORD=${{ secrets.DB_PASSWORD }}
    SESSION_DRIVER=database
    SESSION_LIFETIME=120
    SESSION_ENCRYPT=false
    SESSION_PATH=/
    SESSION_DOMAIN=null
    BROADCAST_CONNECTION=log
    FILESYSTEM_DISK=local
    QUEUE_CONNECTION=database
    CACHE_STORE=database
    # CACHE_PREFIX=
    REDIS_CLIENT=phpredis
    REDIS_HOST=127.0.0.1
    REDIS_PASSWORD=null
    REDIS_PORT=6379
    MAIL_MAILER=log
    MAIL_SCHEME=null
    MAIL_HOST=127.0.0.1
    MAIL_PORT=2525
    MAIL_USERNAME=null
    MAIL_PASSWORD=null
    MAIL_FROM_ADDRESS="hello@example.com"
    MAIL_FROM_NAME="\${APP_NAME}"
    END_OF_ENV
    - name: Install composer dependencies
    run: |
    composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
    - name: Add SSH key
    env:
    SSH_AUTH_SOCK: /tmp/ssh_agent.sock
    run: |
    umask 077 && mkdir -p ~/.ssh
    umask 077 && touch ~/.ssh/github_actions
    echo "${{ secrets.SSH_PRIVATE_KEY }}" >> ~/.ssh/github_actions
    ssh-agent -a $SSH_AUTH_SOCK > /dev/null
    ssh-add ~/.ssh/github_actions
    echo -e "Host *\n\tStrictHostKeyChecking no" >> ~/.ssh/config
    - name: Deploy to ServerPilot app
    env:
    SSH_AUTH_SOCK: /tmp/ssh_agent.sock
    run: |
    set -eux -o pipefail
    COMMIT_SHA=${{ github.sha }}
    COMMIT_SHORT_SHA=${COMMIT_SHA:0:7}
    VERSION=$(date '+%Y%m%d%H%M%S').${COMMIT_SHORT_SHA}
    DIST=dist.${VERSION}
    rsync -a ./ ${{ vars.REMOTE_USER }}@${{ vars.REMOTE_HOST }}:apps/${{ vars.APP_NAME }}/${DIST}/
    ssh ${{ vars.REMOTE_USER }}@${{ vars.REMOTE_HOST }} /bin/bash << EOF
    set -eux -o pipefail
    cd ~/apps/${{ vars.APP_NAME }}/${DIST}
    php artisan optimize
    php artisan migrate
    cd ~/apps/${{ vars.APP_NAME }}
    # If public exists but is not a symlink, move it out of the way.
    [[ ! -L public ]] && [[ -e public ]] && mv public public.bak.$(date '+%Y%m%d%H%M%S')
    # Create a new symlink and then atomically replace the existing symlink.
    ln -s ${DIST}/public public.${VERSION}
    mv --no-target-directory public.${VERSION} public
    # Delete all dist.* directories except the new one.
    shopt -s extglob
    rm -rf dist.!(${VERSION})
    EOF
  2. Add and commit the file .github/workflows/deploy.yaml.
  3. Push your changes to the default branch or merge to your default branch using a PR.

Manually trigger the workflow

The workflow will automatically run when changes are pushed or merged to your repository’s default branch.

To run the workflow manually, do the following.

  1. In GitHub, go to the repository’s Actions page.
  2. Click on the name of the workflow (for example, Deploy Laravel).
  3. Click Run workflow, select the desired branch, then click Run workflow.
  4. Refresh the page to see the new workflow run.
  5. Click on the name of the workflow run to see detailed status.