Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions .docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Dockerfile for Laravel using serversideup/php (API-Only)

#
# Builder Stage (Stays the same)
#
FROM serversideup/php:8.4-cli AS builder
USER root
RUN apt-get update && apt-get install -y \
build-essential \
libpng-dev \
libjpeg62-turbo-dev \
libfreetype6-dev \
locales \
zip \
jpegoptim optipng pngquant gifsicle \
vim \
unzip \
git \
curl
RUN install-php-extensions intl
WORKDIR /var/www/html
USER www-data
RUN git config --global --add safe.directory /var/www/html
COPY --chown=www-data:www-data composer.json composer.lock ./
RUN composer install --optimize-autoloader --no-dev --no-scripts
COPY --chown=www-data:www-data . .
RUN composer run-script post-autoload-dump

# Temporarily use a file-based cache and a non-existent in-memory database
# for the build process, preventing errors when a real DB is not available.
ENV CACHE_DRIVER=array
ENV DB_CONNECTION=sqlite
ENV DB_DATABASE=:memory:
# Clear any previous caches and optimize for production
# This caches configuration and routes for faster performance
#RUN php artisan optimize:clear
RUN php artisan optimize

#
# ---- Final App Stage (Web Server) ----
# This is your final, lean image for the web application.
#
FROM serversideup/php:8.4-fpm-nginx AS api
WORKDIR /var/www/html
USER root
RUN install-php-extensions intl
COPY --from=builder /var/www/html /var/www/html
RUN chown -R www-data:www-data /var/www/html
RUN chmod -R 775 /var/www/html/storage /var/www/html/bootstrap/cache
USER www-data
ENV AUTORUN_ENABLED=true
ENV PHP_OPCACHE_ENABLE=1
ENV AUTORUN_LARAVEL_MIGRATION=true
EXPOSE 8080

# ---- Final Scheduler Stage (CLI Worker) ----
# This is the new, optimized image just for your scheduler.
#
FROM serversideup/php:8.4-fpm-nginx AS scheduler
WORKDIR /var/www/html

# ⬇️ 1. We are the ROOT user by default here. This is correct. ⬇️
USER root

# 3. Copy application code and cron files, still as ROOT.
COPY --from=builder /var/www/html /var/www/html
RUN chown -R www-data:www-data /var/www/html
RUN chmod -R 775 /var/www/html/storage /var/www/html/bootstrap/cache

# 2. Install any additional PHP extensions needed for the scheduler.
RUN install-php-extensions intl

COPY ./.docker/scheduler-entrypoint.sh /var/www/html/scheduler-entrypoint.sh
RUN chmod +x /var/www/html/scheduler-entrypoint.sh

# 4. ⬇️ NOW, switch to the low-privilege user for running the application. ⬇️
USER www-data

ENV AUTORUN_ENABLED=true
ENV PHP_OPCACHE_ENABLE=1

EXPOSE 8080

100 changes: 100 additions & 0 deletions .docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Laravel Docker Environment

This project provides a production-ready Docker setup for a Laravel application using `serversideup/php` images. It includes a multi-stage `Dockerfile` for optimized builds and a `docker-compose.yml` file to orchestrate the application, database, and caching services.

## Prerequisites

- Docker
- Docker Compose

## Setup & Installation

1. **Clone the Repository:**
Clone this project to your local machine.

2. **Configure Environment:**
Create a `.env` file in the project root by copying the example file:
```bash
cp .env.example .env
```
Update the `.env` file with your specific application and database settings. The default database configuration is set up to work with the `docker-compose.yml` file provided.

3. **Generate Application Key:**
If this is a new project, you'll need to generate a Laravel application key. First, start the containers, then run the command.
```bash
# Start containers in the background
docker-compose up -d --build

# Generate the key
docker-compose exec app php artisan key:generate
```

## Usage

- **Build and Start Containers:**
```bash
docker-compose up -d --build
```
Your application will be available at `http://localhost:8080`.

- **Stop Containers:**
```bash
docker-compose down
```

- **Stop Containers and Remove Volumes:** (Use this to start fresh)
```bash
docker-compose down -v
```

## Scheduled Tasks (Cron)

To run Laravel's scheduled tasks, this setup includes a dedicated `scheduler` service in the `docker-compose.yml` file.

This container uses the same application image but runs a loop that executes `php artisan schedule:run` every minute. This is the standard way to handle cron jobs in a containerized Laravel application.

All of your scheduled tasks are defined in the `app/Console/Kernel.php` file as usual. The `scheduler` service will automatically pick up and execute any tasks you define there.

## Laravel Automations (`serversideup/php`)

The `serversideup/php` image provides a powerful automation system that can run common Laravel commands for you when the container starts. This is perfect for production deployments.

### Master Switch

All automations are controlled by a single master switch. They will **only run** if this is enabled.

- **`AUTORUN_ENABLED`**: Set to `"true"` to enable the automation scripts. Defaults to `false`.

### Automation Flags

If `AUTORUN_ENABLED` is set to `"true"`, you can then toggle individual automations.

| Environment Variable | Default | Command Executed | Description |
| ---------------------------------- | ------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `AUTORUN_LARAVEL_MIGRATION` | `true` | `php artisan migrate --force` | Automatically runs your database migrations. |
| `AUTORUN_LARAVEL_CONFIG_CACHE` | `true` | `php artisan config:cache` | Caches your configuration files for a significant performance boost. |
| `AUTORUN_LARAVEL_ROUTE_CACHE` | `true` | `php artisan route:cache` | Caches your route definitions. |
| `AUTORUN_LARAVEL_VIEW_CACHE` | `true` | `php artisan view:cache` | Compiles and caches your Blade templates. |
| `AUTORUN_LARAVEL_EVENT_CACHE` | `true` | `php artisan event:cache` | Caches your application's events and listeners. |
| `AUTORUN_LARAVEL_STORAGE_LINK` | `true` | `php artisan storage:link` | Creates the symbolic link from `public/storage` to `storage/app/public`. |
| `AUTORUN_LARAVEL_MIGRATION_ISOLATION` | `false` | `php artisan migrate --force --isolated` | Ensures only one container runs migrations at a time. Requires Laravel 9.38+ and a locking database. |
| `AUTORUN_LARAVEL_MIGRATION_TIMEOUT` | `30` | N/A | The number of seconds the script will wait for the database to be available before migrating. |

## Manual Artisan Commands

You can run any `artisan` command manually by executing it inside the `app` container.

- **Run Migrations:**
```bash
docker-compose exec app php artisan migrate
```

- **Access Tinker:**
```bash
docker-compose exec app php artisan tinker
```

- **Clear Cache:**
```bash
docker-compose exec app php artisan optimize:clear
```
13 changes: 13 additions & 0 deletions .docker/scheduler-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/sh
set -e

echo "Scheduler entrypoint script started."

while true; do
# Run the Laravel scheduler command.
# We use 'php -d memory_limit=-1' to ensure it doesn't run out of memory.
php -d memory_limit=-1 /var/www/html/artisan schedule:run

# Wait for 60 seconds before the next run.
sleep 60
done
Comment on lines +1 to +13

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not work due to the sleep not taking into account the duration of the schedule run, also, error handling is missing. Either run cron -f as exec argument for the container with a new crontab, or have schedule:run as the exec of the container and run a new instance of the container every 60 seconds via ecs native scheduler

https://laravel.com/docs/12.x/scheduling#running-the-scheduler

https://docs.aws.amazon.com/AmazonECS/latest/developerguide/scheduling_tasks.html

109 changes: 27 additions & 82 deletions .github/workflows/build_and_deploy_dev.yml
Original file line number Diff line number Diff line change
@@ -1,29 +1,3 @@
# This workflow will build and push a new container image to Amazon ECR,
# and then will deploy a new task definition to Amazon ECS, when there is a push to the "master" branch.
#
# To use this workflow, you will need to complete the following set-up steps:
#
# 1. Create an ECR repository to store your images.
# For example: `aws ecr create-repository --repository-name my-ecr-repo --region us-east-2`.
# Replace the value of the `ECR_REPOSITORY` environment variable in the workflow below with your repository's name.
# Replace the value of the `AWS_REGION` environment variable in the workflow below with your repository's region.
#
# 2. Create an ECS task definition, an ECS cluster, and an ECS service.
# For example, follow the Getting Started guide on the ECS console:
# https://us-east-2.console.aws.amazon.com/ecs/home?region=us-east-2#/firstRun
# Replace the value of the `ECS_SERVICE` environment variable in the workflow below with the name you set for the Amazon ECS service.
# Replace the value of the `ECS_CLUSTER` environment variable in the workflow below with the name you set for the cluster.
#
# 3. Store your ECS task definition as a JSON file in your repository.
# The format should follow the output of `aws ecs register-task-definition --generate-cli-skeleton`.
# Replace the value of the `ECS_TASK_DEFINITION` environment variable in the workflow below with the path to the JSON file.
# Replace the value of the `CONTAINER_NAME` environment variable in the workflow below with the name of the container
# in the `containerDefinitions` section of the task definition.
#
# 4. Store an IAM user access key in GitHub Actions secrets named `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`.
# See the documentation for each action used below for the recommended IAM policies for this IAM user,
# and best practices on handling the access key credentials.

name: Deploy to Amazon ECS DEV

on:
Expand All @@ -33,27 +7,27 @@ on:

env:
ENVIRONMENT: dev
AWS_REGION: us-east-1 # set this to your preferred AWS region, e.g. us-west-1
ECR_REPOSITORY: REPLACEPROJECTNAME # set this to your Amazon ECR repository name
ECS_CLUSTER: REPLACEPROJECTNAME # set this to your Amazon ECS cluster name
# ECS_TASK_DEFINITION: MY_ECS_TASK_DEFINITION # set this to the path to your Amazon ECS task definition
# file, e.g. .aws/task-definition.json
CONTAINER_NAME:
REPLACEPROJECTNAME # set this to the name of the container in the
# containerDefinitions section of your task definition
DOCKER_PLATFORM: linux/amd64 # set this to the platform you want to build for
IMAGE_TAG: ${{ github.run_id }}-${{ github.run_attempt }}
REGISTRY_URL: <ECR URL>
AWS_REGION: REPLACEREGION
ECS_CLUSTER: REPLACEPROJECTNAME # Your Amazon ECS cluster name
# The base name for your ECR repositories. We'll append '-app' and '-scheduler'.
ECR_REPOSITORY_BASE: REPLACEPROJECTNAME
DOCKER_PLATFORM: linux/amd64
# A unique tag for this specific workflow run
IMAGE_TAG: ${{ github.sha }}
REGISTRY_URL: ${{ secrets.AWS_ACCOUNT_ID_DEV }}.dkr.ecr.us-east-1.amazonaws.com

permissions:
contents: read
id-token: write

jobs:
setup:
name: Deploy
build-and-deploy:
name: Build and Deploy ${{ matrix.service }}
runs-on: ubuntu-latest
# environment: dev # Works only on paid GH orgs
environment: develop
strategy:
matrix:
service: [api, scheduler]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No queue worker?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didnt have that present, will look into it, is there another side-service i should take into account?
Is it always needed or should be left as optional?


steps:
- name: Checkout
Expand All @@ -62,72 +36,43 @@ jobs:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ vars.AWS_GITHUB_ACTIONS_ROLE }}
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID_DEV }}:role/${{ vars.AWS_GITHUB_ACTIONS_ROLE_DEV }}
aws-region: ${{ env.AWS_REGION }}

- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
platforms: ${{ env.DOCKER_PLATFORM }}

- name: Set environment
uses: SpicyPizza/[email protected]
with:
envkey_VITE_APP_NAME: "${{ vars.VITE_APP_NAME }}"
envkey_VITE_SENTRY_DSN: "${{ vars.VITE_SENTRY_DSN }}"
envkey_VITE_SENTRY_ENVIRONMENT: "${{ vars.VITE_SENTRY_ENVIRONMENT }}"
envkey_VITE_API_URL: "${{ vars.VITE_API_URL }}"
envkey_VITE_APP_ENV: "${{ vars.VITE_APP_ENV }}"
envkey_VITE_APP_URL: "${{ vars.VITE_APP_URL }}"

file_name: .env

- name: Build and push
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
- name: Build and push image
uses: docker/build-push-action@v6
with:
context: .
file: ./.docker/Dockerfile
target: ${{ matrix.service }}
push: true
platforms: ${{ env.DOCKER_PLATFORM }}
tags: ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }}
file: ./deployment/image/Dockerfile

deploy:
needs: setup
runs-on: ubuntu-latest
# environment: dev # Works only on paid GH orgs
strategy:
matrix:
service: [app]
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ vars.AWS_GITHUB_ACTIONS_ROLE }}
aws-region: ${{ env.AWS_REGION }}
tags: ${{ env.REGISTRY_URL }}/${{ env.ECR_REPOSITORY_BASE }}-${{ matrix.service }}:${{ env.IMAGE_TAG }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Download task def
- name: Download task definition
run: |
aws ecs describe-task-definition --task-definition ${{ matrix.service }}_task_definition_${{ env.ECR_REPOSITORY }} --query taskDefinition > task-definition.json
aws ecs describe-task-definition --task-definition app_task_definition_${{ env.ECR_REPOSITORY_BASE }}-${{ matrix.service }} --query taskDefinition > task-definition.json

- name: Fill in the new image ID in the Amazon ECS task definition
- name: Fill in new image ID in task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: ${{ env.CONTAINER_NAME }}
image: ${{ env.REGISTRY_URL }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }}
container-name: sensifai

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

image: ${{ env.REGISTRY_URL }}/${{ env.ECR_REPOSITORY_BASE }}-${{ matrix.service }}:${{ env.IMAGE_TAG }}

- name: Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
service: ${{ matrix.service }}_service_${{ env.ECR_REPOSITORY }}
service: app_service_${{ env.ECR_REPOSITORY_BASE }}-${{ matrix.service }}
cluster: ${{ env.ECS_CLUSTER }}
wait-for-service-stability: false
Loading