chore: Improve .dev dockerfile for development and production (#183)

* chore: update dockerfile and dev env

* chore(dockerfile): fix user/group id args

* chore(docker): use php-fpm w/ separate nginx

* chore(docker): add nginx image w/ static files

* chore(docker): build vite resources only once, bump vite minor version,
add watch yarn command.
By using --buildplatform tag in the dockerfile we can have the vite step
be built only on the host platform, which significantly speeds it up.
This is possible since the build assets aren't platform dependant.

* Move dockerfiles to .dev
This commit is contained in:
Tim van Osch
2025-08-31 00:46:56 +02:00
committed by GitHub
parent d5137e393d
commit d1bca362de
15 changed files with 2839 additions and 204 deletions

35
.dev/Dockerfile Normal file
View File

@@ -0,0 +1,35 @@
FROM --platform=$BUILDPLATFORM node AS static_builder
WORKDIR /var/www/html
COPY . /var/www/html
RUN yarn && yarn build
FROM serversideup/php:8-fpm-alpine AS base
USER root
RUN install-php-extensions exif
RUN install-php-extensions pgsql
RUN install-php-extensions sqlite3
RUN install-php-extensions imagick
RUN install-php-extensions mbstring
RUN install-php-extensions gd
RUN install-php-extensions xml
RUN install-php-extensions zip
RUN install-php-extensions redis
RUN install-php-extensions bcmath
RUN install-php-extensions intl
RUN install-php-extensions curl
FROM base AS development
ARG UID
ARG GID
USER root
RUN docker-php-serversideup-set-id www-data $UID:$GID
USER www-data
FROM base AS production
ENV AUTORUN_ENABLED=true
COPY --from=static_builder --chown=www-data:www-data /var/www/html/public /var/www/html/public
COPY --chown=www-data:www-data . /var/www/html
RUN composer install --prefer-dist
USER www-data

View File

@@ -88,10 +88,10 @@ docker compose -f .dev/docker-compose.mysql.yml down
To correctly run `composer`, `npm`, `artisan`, `pint`, `pest` or other binaries within this project, you must ssh into the container as follows:
```
docker exec -it --user invoiceshelf invoiceshelf-dev-php /bin/bash
docker exec -it invoiceshelf /bin/bash
```
In the `/home/invoiceshelf/app` directory you can find the application root and run the commands from there.
In the `/var/www/html` directory you can find the application root and run the commands from there.
## What is included
@@ -113,7 +113,7 @@ The setup parameters/credentials for each of the supported databases are as foll
|---|---|---|---|
| **DB_USER** | invoiceshelf | invoiceshelf | Not applicable |
| **DB_PASS** | invoiceshelf | invoiceshelf | Not applicable |
| **DB_NAME** | invoiceshelf | invoiceshelf | /home/invoiceshelf/database/database.sqlite |
| **DB_NAME** | invoiceshelf | invoiceshelf | /var/www/html/database/database.sqlite |
| **DB_HOST** | db-mysql | db-pgsql | Not applicable |
| **DB_PORT** | 3036 | 5432 | Not applicable |
@@ -164,12 +164,3 @@ To utilize Mailpit, use the following credentials:
If you have any questions, feel free to open issue.

View File

@@ -2,28 +2,32 @@ services:
php-fpm:
container_name: invoiceshelf-dev-php
build:
context: ./php
dockerfile: Dockerfile
context: ..
dockerfile: .dev/Dockerfile
args:
- UID=${USRID:-1000}
- GID=${GRPID:-1000}
ports:
- 5173:5173
target: development
volumes:
- ../:/home/invoiceshelf/app
- ../:/var/www/html
networks:
- invoiceshelf-dev
nginx:
container_name: invoiceshelf-dev-nginx
image: nginx:stable
build:
context: ..
dockerfile: .dev/nginx.Dockerfile
environment:
- "PHP_FPM_HOST=php-fpm:9000"
ports:
- '80:80'
volumes:
- ./nginx/conf.d/dev.conf:/etc/nginx/conf.d/dev.conf
- ../:/home/invoiceshelf/app
- ../:/var/www/html
networks:
- invoiceshelf-dev
invoiceshelf-dev:
aliases:
- invoiceshelf.test
db:
image: mariadb:10.9

View File

@@ -2,28 +2,32 @@ services:
php-fpm:
container_name: invoiceshelf-dev-php
build:
context: ./php
dockerfile: Dockerfile
context: ..
dockerfile: .dev/Dockerfile
args:
- UID=${USRID:-1000}
- GID=${GRPID:-1000}
ports:
- 5173:5173
target: development
volumes:
- ../:/home/invoiceshelf/app
- ../:/var/www/html
networks:
- invoiceshelf-dev
nginx:
container_name: invoiceshelf-dev-nginx
image: nginx:stable
build:
context: ..
dockerfile: .dev/nginx.Dockerfile
environment:
- "PHP_FPM_HOST=php-fpm:9000"
ports:
- '80:80'
volumes:
- ./nginx/conf.d/dev.conf:/etc/nginx/conf.d/dev.conf
- ../:/home/invoiceshelf/app
- ../:/var/www/html
networks:
- invoiceshelf-dev
invoiceshelf-dev:
aliases:
- invoiceshelf.test
db:
image: postgres:15

View File

@@ -2,28 +2,32 @@ services:
php-fpm:
container_name: invoiceshelf-dev-php
build:
context: ./php
dockerfile: Dockerfile
context: ..
dockerfile: .dev/Dockerfile
args:
- UID=${USRID:-1000}
- GID=${GRPID:-1000}
target: development
volumes:
- ../:/home/invoiceshelf/app
ports:
- 5173:5173
- ../:/var/www/html
networks:
- invoiceshelf-dev
nginx:
container_name: invoiceshelf-dev-nginx
image: nginx:stable
build:
context: ..
dockerfile: .dev/nginx.Dockerfile
environment:
- "PHP_FPM_HOST=php-fpm:9000"
ports:
- '80:80'
volumes:
- ./nginx/conf.d/dev.conf:/etc/nginx/conf.d/dev.conf
- ../:/home/invoiceshelf/app
- ../:/var/www/html
networks:
- invoiceshelf-dev
invoiceshelf-dev:
aliases:
- invoiceshelf.test
adminer:
container_name: invoiceshelf-dev-adminer
@@ -43,10 +47,9 @@ services:
mail:
container_name: invoiceshelf-dev-mailpit
image: axllent/mailpit:latest
restart: always
ports:
- 1025:1025
- 8025:8025
- '1025:1025'
- '8025:8025'
networks:
- invoiceshelf-dev

57
.dev/nginx.Dockerfile Normal file
View File

@@ -0,0 +1,57 @@
FROM --platform=$BUILDPLATFORM node AS static_builder
WORKDIR /var/www/html
COPY . /var/www/html
RUN yarn && yarn build
FROM nginx AS production
ENV PHP_FPM_HOST="php-fpm:9000"
COPY --chown=www-data:www-data . /var/www/html
COPY --from=static_builder --chown=www-data:www-data /var/www/html/public /var/www/html/public
# Map the PHP-FPM host from the PHP_FPM_HOST environment variable to an nging variable
RUN mkdir /etc/nginx/templates && cat <<EOF > /etc/nginx/templates/20-invoiceshelf.conf.template
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html/public;
# Set allowed "index" files
index index.html index.htm index.php;
server_name _;
charset utf-8;
# Set max upload to 2048M
client_max_body_size 2048M;
# Healthchecks: Set /healthcheck to be the healthcheck URL
location /healthcheck {
access_log off;
# set max 5 seconds for healthcheck
fastcgi_read_timeout 5s;
include fastcgi_params;
fastcgi_param SCRIPT_NAME /healthcheck;
fastcgi_param SCRIPT_FILENAME /healthcheck;
fastcgi_pass \${PHP_FPM_HOST};
}
# Have NGINX try searching for PHP files as well
location / {
try_files \$uri \$uri/ /index.php?\$query_string;
}
# Pass "*.php" files to PHP-FPM
location ~ \.php\$ {
fastcgi_pass \${PHP_FPM_HOST};
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
include fastcgi_params;
fastcgi_buffers 8 8k;
fastcgi_buffer_size 8k;
}
}
EOF

View File

@@ -1,22 +0,0 @@
server {
listen 80;
root /home/invoiceshelf/app/public;
index index.php;
error_log /var/log/nginx/dev-error.log;
access_log /var/log/nginx/dev-access.log;
server_name invoiceshelf.test;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass php-fpm:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
}

View File

@@ -1,64 +0,0 @@
FROM php:8.3-fpm-bookworm
ARG UID
ARG GID
ENV UID=${UID}
ENV GID=${GID}
USER root
# Create user/group
RUN addgroup --gid ${GID} --system invoiceshelf && \
adduser --gid ${GID} --system --disabled-password --shell /bin/sh -u ${UID} --home /home/invoiceshelf invoiceshelf && \
sed -i "s/user = www-data/user = invoiceshelf/g" /usr/local/etc/php-fpm.d/www.conf && \
sed -i "s/group = www-data/group = invoiceshelf/g" /usr/local/etc/php-fpm.d/www.conf
# Install composer & npm
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer && \
curl -sL https://deb.nodesource.com/setup_20.x | bash - && \
apt install -y nodejs
# install dependencies
RUN apt update && apt install -y \
libpng-dev \
zlib1g-dev \
libxml2-dev \
libzip-dev \
libonig-dev \
libpq-dev \
sqlite3 \
postgresql-client \
mariadb-client \
zip \
curl \
unzip \
webp \
&& docker-php-ext-configure gd \
&& docker-php-ext-install -j$(nproc) gd \
&& docker-php-ext-install bcmath \
&& docker-php-ext-install mbstring \
&& docker-php-ext-install mysqli \
&& docker-php-ext-install pdo_mysql \
&& docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql \
&& docker-php-ext-install pgsql \
&& docker-php-ext-install pdo_pgsql \
&& docker-php-ext-install zip \
&& docker-php-ext-install xml \
&& docker-php-ext-install exif \
&& docker-php-source delete
# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Set workdir
WORKDIR /home/invoiceshelf/app
# Copy Files
COPY entrypoint.sh /entrypoint.sh
# Entrypoint
ENTRYPOINT ["/entrypoint.sh"]
# Launch php-fpm
CMD ["php-fpm"]

View File

@@ -1,44 +0,0 @@
#!/bin/bash
echo "############################################"
echo "### InvoiceShelf Development Environment ###"
echo "############################################"
cd /home/invoiceshelf/app
# Composer build
if [ ! -d vendor ]; then
composer install
fi
# Empty sqlite database
if [ ! -f database/database.sqlite ]; then
cp database/stubs/sqlite.empty.db database/database.sqlite
fi
# .env file set up
if [ ! -f .env ]; then
cp .env.example .env
php artisan key:generate --force
fi
# NPM build
if [ ! -d node_modules ]; then
npm install
npm run build
fi
# Storage symlink
php artisan storage:link
# Permissions
chmod 775 storage/framework
chmod 775 storage/logs
chmod 775 bootstrap/cache
chown -R ${UID}:${GID} /home/invoiceshelf/app
chmod +x artisan
echo "Entrypoint complete."
exec $@

36
.dockerignore Normal file
View File

@@ -0,0 +1,36 @@
.dev/
.git/
.github/
database/*.sqlite
node_modules/
storage/app/*
storage/fonts/*
storage/framework/cache/data/*
storage/framework/sessions/*
storage/framework/views/*
storage/logs/*
tests/
vendor/
.dockerignore
.editorconfig
.env
.env.example
.env.testing
.eslintrc.mjs
.gitattributes
.gitignore
.prettierrc.json
CODE_OF_CONDUCT.md
Dockerfile
*.Dockerfile
LICENSE
Makefile
SECURITY.md
_ide_helper.php
crowdin.yml
invoiceshelf.code-workspace
package-lock.json
phpunit.xml
readme.md
version.md

View File

@@ -3,6 +3,7 @@
"type": "module",
"scripts": {
"dev": "vite",
"watch": "vite build --watch",
"build": "vite build",
"serve": "vite preview",
"test": "eslint ./resources/scripts --ext .js,.vue"

2
tailwind.config.js vendored
View File

@@ -62,7 +62,7 @@ export default {
)}")`,
}),
},
fontFamily: {
base: ['Poppins', 'sans-serif'],
},

68
vite.config.js vendored
View File

@@ -1,39 +1,45 @@
import {defineConfig} from 'vite';
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
resolve: {
alias: {
"vue-i18n": "vue-i18n/dist/vue-i18n.cjs.js",
'@': resolve(__dirname, './resources/'),
$fonts: resolve(__dirname, './resources/static/fonts'),
$images: resolve(__dirname, './resources/static/img')
},
extensions: ['.js', '.ts', '.jsx', '.tsx', '.json', '.vue', '.mjs']
server: {
host: 'invoiceshelf.test',
hmr: {
host: 'invoiceshelf.test',
}
},
resolve: {
alias: {
"vue-i18n": "vue-i18n/dist/vue-i18n.cjs.js",
'@': resolve(__dirname, './resources/'),
$fonts: resolve(__dirname, './resources/static/fonts'),
$images: resolve(__dirname, './resources/static/img')
},
plugins: [
vue({
template: {
transformAssetUrls: {
// The Vue plugin will re-write asset URLs, when referenced
// in Single File Components, to point to the Laravel web
// server. Setting this to `null` allows the Laravel plugin
// to instead re-write asset URLs to point to the Vite
// server instead.
base: null,
extensions: ['.js', '.ts', '.jsx', '.tsx', '.json', '.vue', '.mjs']
},
plugins: [
vue({
template: {
transformAssetUrls: {
// The Vue plugin will re-write asset URLs, when referenced
// in Single File Components, to point to the Laravel web
// server. Setting this to `null` allows the Laravel plugin
// to instead re-write asset URLs to point to the Vite
// server instead.
base: null,
// The Vue plugin will parse absolute URLs and treat them
// as absolute paths to files on disk. Setting this to
// `false` will leave absolute URLs un-touched so they can
// reference assets in the public directory as expected.
includeAbsolute: false,
},
},
}),
laravel([
'resources/scripts/main.js'
])
]
// The Vue plugin will parse absolute URLs and treat them
// as absolute paths to files on disk. Setting this to
// `false` will leave absolute URLs un-touched so they can
// reference assets in the public directory as expected.
includeAbsolute: false,
},
},
}),
laravel([
'resources/scripts/main.js'
])
]
});

2628
yarn.lock Normal file

File diff suppressed because it is too large Load Diff