diff --git a/docker/.env.docker.example b/docker/.env.docker.example new file mode 100644 index 00000000..0fe8e7d6 --- /dev/null +++ b/docker/.env.docker.example @@ -0,0 +1,177 @@ +APP_NAME="TryPost" +APP_ENV=local +APP_KEY= +APP_DEBUG=true +APP_URL=http://localhost:8000 + +# Self-hosted mode (skips payment requirements) +SELF_HOSTED=true + +TELESCOPE_ENABLED=false + +APP_LOCALE=en +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=en_US + +APP_MAINTENANCE_DRIVER=file + +BCRYPT_ROUNDS=12 + +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +# Database — service name "pgsql" inside the Compose network. +DB_CONNECTION=pgsql +DB_HOST=pgsql +DB_PORT=5432 +DB_DATABASE=trypost +DB_USERNAME=postgres +DB_PASSWORD=password + +# Session +SESSION_DRIVER=database +SESSION_LIFETIME=1440 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=null + +# Broadcasting, Queue, Cache — all Redis-backed in Docker. +BROADCAST_CONNECTION=reverb +QUEUE_CONNECTION=redis +CACHE_STORE=redis + +# File Storage — local-disk by default; switch to s3/r2 for production. +FILESYSTEM_DISK=local + +# Redis — service name "redis" inside the Compose network. +REDIS_HOST=redis +REDIS_PASSWORD=null +REDIS_PORT=6379 + +# Mail — service name "mailpit" inside the Compose network. UI at :8025. +MAIL_MAILER=smtp +MAIL_HOST=mailpit +MAIL_PORT=1025 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null +MAIL_FROM_ADDRESS="hello@example.com" +MAIL_FROM_NAME="${APP_NAME}" + +# Reverb (WebSockets) — host=localhost is browser-perspective via host port-forward. +REVERB_APP_ID=1001 +REVERB_APP_KEY=trypost-reverb-key +REVERB_APP_SECRET=trypost-reverb-secret +REVERB_HOST=localhost +REVERB_PORT=8080 +REVERB_SCHEME=http + +# AWS S3 (set FILESYSTEM_DISK=s3) +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_URL= + +# Cloudflare R2 (set FILESYSTEM_DISK=r2) +R2_ACCESS_KEY_ID= +R2_SECRET_ACCESS_KEY= +R2_ENDPOINT= +R2_REGION=auto +R2_BUCKET= +R2_URL= + +# ============================================ +# Social Platform Credentials (optional in dev) +# ============================================ +LINKEDIN_CLIENT_ID= +LINKEDIN_CLIENT_SECRET= +LINKEDIN_CLIENT_REDIRECT="${APP_URL}/accounts/linkedin/callback" +LINKEDIN_PAGE_CLIENT_REDIRECT="${APP_URL}/accounts/linkedin-page/callback" + +X_CLIENT_ID= +X_CLIENT_SECRET= +X_CLIENT_REDIRECT="${APP_URL}/accounts/x/callback" + +TIKTOK_CLIENT_ID= +TIKTOK_CLIENT_SECRET= +TIKTOK_CLIENT_REDIRECT="${APP_URL}/accounts/tiktok/callback" + +FACEBOOK_CLIENT_ID= +FACEBOOK_CLIENT_SECRET= +FACEBOOK_CLIENT_REDIRECT="${APP_URL}/accounts/facebook/callback" + +INSTAGRAM_CLIENT_ID= +INSTAGRAM_CLIENT_SECRET= +INSTAGRAM_CLIENT_REDIRECT="${APP_URL}/accounts/instagram/callback" + +THREADS_CLIENT_ID= +THREADS_CLIENT_SECRET= +THREADS_CLIENT_REDIRECT="${APP_URL}/accounts/threads/callback" + +GOOGLE_AUTH_ENABLED=false +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= +GOOGLE_CLIENT_REDIRECT="${APP_URL}/accounts/youtube/callback" +GOOGLE_AUTH_CALLBACK="${APP_URL}/auth/google/callback" + +GITHUB_AUTH_ENABLED=false +GITHUB_CLIENT_ID= +GITHUB_CLIENT_SECRET= +GITHUB_AUTH_CALLBACK="${APP_URL}/auth/github/callback" + +PINTEREST_CLIENT_ID= +PINTEREST_CLIENT_SECRET= +PINTEREST_CLIENT_REDIRECT="${APP_URL}/accounts/pinterest/callback" + +# AI Services +OPENAI_API_KEY= +ANTHROPIC_API_KEY= +GEMINI_API_KEY= +ELEVENLABS_API_KEY= + +AI_TEXT_PROVIDER=openai +AI_TEXT_MODEL=gpt-5.4 +AI_IMAGE_PROVIDER=openai +AI_AUDIO_PROVIDER=elevenlabs + +# Stripe (only required when SELF_HOSTED=false) +STRIPE_KEY= +STRIPE_SECRET= +STRIPE_WEBHOOK_SECRET= + +STRIPE_STARTER_MONTHLY= +STRIPE_STARTER_YEARLY= +STRIPE_PLUS_MONTHLY= +STRIPE_PLUS_YEARLY= +STRIPE_PRO_MONTHLY= +STRIPE_PRO_YEARLY= +STRIPE_MAX_MONTHLY= +STRIPE_MAX_YEARLY= + +# Telemetry — disabled by default in Docker dev. +NIGHTWATCH_ENABLED=false +NIGHTWATCH_TOKEN= + +# Media Services +UNSPLASH_ACCESS_KEY= +UNSPLASH_SECRET_KEY= +GIPHY_API_KEY= + +# Analytics (optional) +GTM_ID= +POSTHOG_ENABLED=false +POSTHOG_API_KEY= +POSTHOG_HOST=https://us.i.posthog.com + +# Vite +VITE_APP_NAME="${APP_NAME}" +VITE_REVERB_APP_KEY="${REVERB_APP_KEY}" +VITE_REVERB_HOST="${REVERB_HOST}" +VITE_REVERB_PORT="${REVERB_PORT}" +VITE_REVERB_SCHEME="${REVERB_SCHEME}" +VITE_POSTHOG_ENABLED="${POSTHOG_ENABLED}" +VITE_POSTHOG_API_KEY="${POSTHOG_API_KEY}" +VITE_POSTHOG_HOST="${POSTHOG_HOST}" diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 00000000..5025c384 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,179 @@ +# syntax=docker/dockerfile:1.7 + +# ---------------------------------------------------------------------------- +# TryPost Dockerfile — multi-stage with `dev` and `production` targets. +# +# docker build --target dev -t trypost:dev -f docker/Dockerfile . +# docker build --target production -t trypost:prod -f docker/Dockerfile . +# ---------------------------------------------------------------------------- + +ARG PHP_VERSION=8.4 +ARG NODE_VERSION=22 + +# ---------------------------------------------------------------------------- +# Stage 1: system-base — PHP-FPM + system packages + extensions +# ---------------------------------------------------------------------------- +FROM php:${PHP_VERSION}-fpm-alpine AS system-base + +RUN apk add --no-cache \ + nginx \ + supervisor \ + bash \ + curl \ + git \ + unzip \ + shadow \ + tzdata \ + postgresql-client \ + postgresql-dev \ + libpng-dev \ + libjpeg-turbo-dev \ + freetype-dev \ + libzip-dev \ + oniguruma-dev \ + icu-dev \ + linux-headers \ + $PHPIZE_DEPS \ + && docker-php-ext-configure gd --with-freetype --with-jpeg \ + && docker-php-ext-install -j"$(nproc)" \ + pdo_pgsql \ + pgsql \ + gd \ + zip \ + opcache \ + bcmath \ + exif \ + pcntl \ + intl \ + sockets \ + && pecl install redis \ + && docker-php-ext-enable redis \ + && apk del $PHPIZE_DEPS \ + && rm -rf /tmp/* /var/cache/apk/* + +COPY --from=composer:2 /usr/bin/composer /usr/bin/composer + +WORKDIR /var/www/html + +# ---------------------------------------------------------------------------- +# Stage 2: composer-deps — full PHP deps (incl. dev) for tooling/asset build +# ---------------------------------------------------------------------------- +FROM system-base AS composer-deps + +COPY composer.json composer.lock ./ +RUN composer install \ + --no-scripts \ + --no-autoloader \ + --prefer-dist \ + --no-interaction + +# ---------------------------------------------------------------------------- +# Stage 3: composer-deps-prod — production deps only (no dev tooling) +# ---------------------------------------------------------------------------- +FROM system-base AS composer-deps-prod + +COPY composer.json composer.lock ./ +RUN composer install \ + --no-dev \ + --no-scripts \ + --no-autoloader \ + --prefer-dist \ + --no-interaction + +# ---------------------------------------------------------------------------- +# Stage 4: wayfinder-gen — generate TS route helpers (needs PHP + Laravel) +# ---------------------------------------------------------------------------- +FROM composer-deps AS wayfinder-gen + +COPY --from=composer-deps /var/www/html/vendor ./vendor +COPY . . + +# Stub env so artisan can boot; overridden at runtime. +ENV APP_KEY=base64:c3R1Yi13YXlmaW5kZXItZ2VuLWtleS1mb3ItYnVpbGRpbmctYXNzZXRzMA== \ + APP_ENV=production \ + APP_DEBUG=false + +RUN composer dump-autoload --no-scripts --optimize \ + && php artisan wayfinder:generate --with-form + +# ---------------------------------------------------------------------------- +# Stage 5: asset-build — Vite + Inertia SSR build +# ---------------------------------------------------------------------------- +FROM node:${NODE_VERSION}-alpine AS asset-build + +WORKDIR /var/www/html + +COPY package.json package-lock.json ./ +RUN npm ci --no-audit --no-fund + +# Pull source + freshly generated wayfinder TS from previous stage. +COPY --from=wayfinder-gen /var/www/html /var/www/html + +RUN npm run build && npm run build:ssr + +# ---------------------------------------------------------------------------- +# Stage 6: dev — local development image (bind-mount the source at runtime) +# ---------------------------------------------------------------------------- +FROM system-base AS dev + +ARG UID=1000 +ARG GID=1000 + +# Node + npm for in-container Vite, npm scripts, ad-hoc tooling. +RUN apk add --no-cache nodejs npm + +# Create non-root app user matching host UID/GID for clean bind-mount writes. +RUN groupmod -g "${GID}" www-data 2>/dev/null || groupadd -g "${GID}" app \ + && (id -u app >/dev/null 2>&1 || useradd -u "${UID}" -g "${GID}" -d /home/app -m -s /bin/bash app) \ + && chown -R "${UID}:${GID}" /var/www/html + +COPY docker/nginx.conf /etc/nginx/http.d/default.conf +COPY docker/php.dev.ini /usr/local/etc/php/conf.d/99-trypost.ini +COPY docker/supervisord.dev.conf /etc/supervisor/conf.d/supervisord.conf +COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh +COPY docker/.env.docker.example /var/www/html/.env.docker.example +RUN chmod +x /usr/local/bin/entrypoint.sh + +ENV TRYPOST_TARGET=dev + +EXPOSE 80 5173 8080 + +HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=5 \ + CMD curl -fsS http://127.0.0.1/up || exit 1 + +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] + +# ---------------------------------------------------------------------------- +# Stage 7: production — self-contained image for self-hosters +# ---------------------------------------------------------------------------- +FROM system-base AS production + +# Non-root runtime user (fixed UID; overridable per orchestrator if needed). +RUN addgroup -g 1000 -S app \ + && adduser -u 1000 -S app -G app -h /home/app + +COPY docker/nginx.conf /etc/nginx/http.d/default.conf +COPY docker/php.prod.ini /usr/local/etc/php/conf.d/99-trypost.ini +COPY docker/supervisord.prod.conf /etc/supervisor/conf.d/supervisord.conf +COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh +RUN chmod +x /usr/local/bin/entrypoint.sh + +# Production composer deps only. +COPY --from=composer-deps-prod /var/www/html/vendor /var/www/html/vendor + +# Application source + generated wayfinder TS + built assets. +COPY --from=wayfinder-gen /var/www/html /var/www/html +COPY --from=asset-build /var/www/html/public/build /var/www/html/public/build +COPY --from=asset-build /var/www/html/bootstrap/ssr /var/www/html/bootstrap/ssr + +RUN composer dump-autoload --optimize --classmap-authoritative --no-scripts \ + && chown -R app:app /var/www/html/storage /var/www/html/bootstrap/cache + +ENV TRYPOST_TARGET=production + +EXPOSE 80 8080 + +HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ + CMD curl -fsS http://127.0.0.1/up || exit 1 + +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100755 index 00000000..56d24bfd --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,110 @@ +#!/bin/sh +# TryPost — container entrypoint. Idempotent first-run setup, then exec supervisord. + +set -e + +cd /var/www/html + +TARGET="${TRYPOST_TARGET:-dev}" + +# 1) Bootstrap .env from the Docker template on first dev boot. The bind-mount +# in dev hides /var/www/html/.env.docker.example, so prefer docker/ first. +if [ "${TRYPOST_DOCKER_BOOTSTRAP:-0}" = "1" ] && [ ! -f .env ]; then + if [ -f docker/.env.docker.example ]; then + echo "[entrypoint] seeding .env from docker/.env.docker.example" + cp docker/.env.docker.example .env + elif [ -f .env.docker.example ]; then + echo "[entrypoint] seeding .env from .env.docker.example" + cp .env.docker.example .env + fi + # Hand the seeded .env over to the host user so they can edit it. + chown "${UID:-1000}:${GID:-1000}" .env 2>/dev/null || true +fi + +# 2) Skip-bootstrap escape hatch for advanced users. +if [ "${TRYPOST_SKIP_BOOTSTRAP:-0}" = "1" ]; then + echo "[entrypoint] TRYPOST_SKIP_BOOTSTRAP=1 — exec'ing supervisord without setup" + exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf +fi + +# 3) Re-install composer deps if vendor was wiped (down -v in dev). +if [ "${TARGET}" = "dev" ] && [ ! -f vendor/autoload.php ]; then + echo "[entrypoint] vendor/ missing — running composer install" + composer install --no-interaction --prefer-dist +fi + +# 4) Re-install node_modules if empty/wiped (dev only; vite needs them). +# Anonymous volumes pre-create the dir, so check for npm's lockfile instead. +if [ "${TARGET}" = "dev" ] && [ ! -f node_modules/.package-lock.json ]; then + echo "[entrypoint] node_modules/ empty — running npm ci" + npm ci --no-audit --no-fund +fi + +# 5) APP_KEY — generate on first boot if blank. +if [ -f .env ] && ! grep -qE '^APP_KEY=base64:' .env; then + echo "[entrypoint] generating APP_KEY" + php artisan key:generate --force +fi + +# 6) Wait for Postgres to be reachable. +DB_HOST_VALUE="${DB_HOST:-pgsql}" +DB_PORT_VALUE="${DB_PORT:-5432}" +DB_USER_VALUE="${DB_USERNAME:-postgres}" +DB_NAME_VALUE="${DB_DATABASE:-trypost}" + +echo "[entrypoint] waiting for postgres at ${DB_HOST_VALUE}:${DB_PORT_VALUE}" +WAIT_ATTEMPTS=0 +until pg_isready -h "${DB_HOST_VALUE}" -p "${DB_PORT_VALUE}" -U "${DB_USER_VALUE}" -d "${DB_NAME_VALUE}" >/dev/null 2>&1; do + WAIT_ATTEMPTS=$((WAIT_ATTEMPTS + 1)) + if [ "${WAIT_ATTEMPTS}" -gt 60 ]; then + echo "[entrypoint] postgres not reachable after 60s — continuing anyway" + break + fi + sleep 1 +done + +# 7) Run migrations (graceful: succeeds even when nothing to migrate). +echo "[entrypoint] running migrations" +php artisan migrate --force --graceful || true + +# 8) storage:link if missing. +if [ ! -L public/storage ]; then + echo "[entrypoint] linking storage" + php artisan storage:link --force || true +fi + +# 9) Passport keys on first boot. +if [ ! -f storage/oauth-private.key ]; then + echo "[entrypoint] generating Passport keys" + php artisan passport:keys --force || true +fi + +# 10) Wayfinder TS regen — Vite needs the files before it boots. +echo "[entrypoint] regenerating wayfinder helpers" +php artisan wayfinder:generate --with-form || true + +# 11) Cache strategy: prod = pre-cache; dev = clear. +if [ "${TARGET}" = "production" ]; then + php artisan config:cache + php artisan route:cache + php artisan view:cache + php artisan event:cache +else + php artisan config:clear + php artisan route:clear + php artisan view:clear + php artisan event:clear +fi + +# 12) Permissions. +if [ "${TARGET}" = "production" ]; then + chown -R app:app storage bootstrap/cache +else + # Dev: ensure UID-mapped user owns runtime dirs. + APP_UID="${UID:-1000}" + APP_GID="${GID:-1000}" + chown -R "${APP_UID}:${APP_GID}" storage bootstrap/cache 2>/dev/null || true +fi + +echo "[entrypoint] ready — handing off to supervisord" +exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf diff --git a/docker/nginx.conf b/docker/nginx.conf new file mode 100644 index 00000000..0682a919 --- /dev/null +++ b/docker/nginx.conf @@ -0,0 +1,31 @@ +server { + listen 80; + listen [::]:80; + server_name _; + root /var/www/html/public; + index index.php; + + charset utf-8; + client_max_body_size 100M; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location = /favicon.ico { access_log off; log_not_found off; } + location = /robots.txt { access_log off; log_not_found off; } + + error_page 404 /index.php; + + location ~ \.php$ { + fastcgi_pass 127.0.0.1:9000; + fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + fastcgi_read_timeout 120s; + include fastcgi_params; + fastcgi_hide_header X-Powered-By; + } + + location ~ /\.(?!well-known).* { + deny all; + } +} diff --git a/docker/php.dev.ini b/docker/php.dev.ini new file mode 100644 index 00000000..58eb5391 --- /dev/null +++ b/docker/php.dev.ini @@ -0,0 +1,22 @@ +; TryPost — PHP settings for local development. +memory_limit = 512M +upload_max_filesize = 100M +post_max_size = 100M +max_execution_time = 120 +max_input_vars = 5000 + +; Hot-reload PHP edits without restarting FPM. +opcache.enable = 1 +opcache.memory_consumption = 128 +opcache.max_accelerated_files = 10000 +opcache.validate_timestamps = 1 +opcache.revalidate_freq = 0 + +; Surface errors loudly in dev. +display_errors = On +display_startup_errors = On +error_reporting = E_ALL +log_errors = On +error_log = /proc/self/fd/2 + +; Xdebug optional — enable via XDEBUG_MODE in compose.override.yaml. diff --git a/docker/php.prod.ini b/docker/php.prod.ini new file mode 100644 index 00000000..9152b763 --- /dev/null +++ b/docker/php.prod.ini @@ -0,0 +1,20 @@ +; TryPost — PHP settings for production. +memory_limit = 256M +upload_max_filesize = 100M +post_max_size = 100M +max_execution_time = 60 +max_input_vars = 5000 + +; OpCache hardened for production: no timestamp checks, larger interned-string buffer. +opcache.enable = 1 +opcache.memory_consumption = 192 +opcache.max_accelerated_files = 20000 +opcache.validate_timestamps = 0 +opcache.interned_strings_buffer = 16 + +; Security +expose_php = Off +display_errors = Off +display_startup_errors = Off +log_errors = On +error_log = /proc/self/fd/2 diff --git a/docker/postgres-init.sh b/docker/postgres-init.sh new file mode 100755 index 00000000..5d3d2ad8 --- /dev/null +++ b/docker/postgres-init.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# Creates the test database expected by phpunit.xml on first Postgres boot. +# Postgres-alpine runs every *.sh / *.sql in /docker-entrypoint-initdb.d/ once, +# right after the primary database (POSTGRES_DB) is initialized. + +set -e + +psql -v ON_ERROR_STOP=1 --username "${POSTGRES_USER}" --dbname "${POSTGRES_DB}" <<-EOSQL + CREATE DATABASE trypost_test; + GRANT ALL PRIVILEGES ON DATABASE trypost_test TO "${POSTGRES_USER}"; +EOSQL diff --git a/docker/supervisord.dev.conf b/docker/supervisord.dev.conf new file mode 100644 index 00000000..983aaa09 --- /dev/null +++ b/docker/supervisord.dev.conf @@ -0,0 +1,69 @@ +[supervisord] +nodaemon=true +user=root +logfile=/dev/null +logfile_maxbytes=0 +pidfile=/run/supervisord.pid + +[program:php-fpm] +command=php-fpm -F +autostart=true +autorestart=true +priority=10 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:nginx] +command=nginx -g 'daemon off;' +autostart=true +autorestart=true +priority=20 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:reverb] +command=php /var/www/html/artisan reverb:start --host=0.0.0.0 --port=8080 +autostart=true +autorestart=true +priority=30 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:horizon] +command=php /var/www/html/artisan horizon +autostart=true +autorestart=true +priority=30 +stopwaitsecs=10 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:scheduler] +command=php /var/www/html/artisan schedule:work +autostart=true +autorestart=true +priority=40 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:vite] +command=/bin/sh -c "exec /usr/bin/npm run dev -- --host 0.0.0.0" +directory=/var/www/html +autostart=true +autorestart=true +startsecs=10 +priority=50 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/docker/supervisord.prod.conf b/docker/supervisord.prod.conf new file mode 100644 index 00000000..95e29ddb --- /dev/null +++ b/docker/supervisord.prod.conf @@ -0,0 +1,58 @@ +[supervisord] +nodaemon=true +user=root +logfile=/dev/null +logfile_maxbytes=0 +pidfile=/run/supervisord.pid + +[program:php-fpm] +command=php-fpm -F +autostart=true +autorestart=true +priority=10 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:nginx] +command=nginx -g 'daemon off;' +autostart=true +autorestart=true +priority=20 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:reverb] +command=php /var/www/html/artisan reverb:start --host=0.0.0.0 --port=8080 +autostart=true +autorestart=unexpected +exitcodes=0,2 +priority=30 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:horizon] +command=php /var/www/html/artisan horizon +autostart=true +autorestart=true +priority=30 +stopwaitsecs=10 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:scheduler] +command=php /var/www/html/artisan schedule:work +autostart=true +autorestart=true +priority=40 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0