Compare commits

..

1 Commits

Author SHA1 Message Date
Lance Pioch
9ecb47ba16 docker: send all supervised process output to the container log
Only Caddy's output reached docker logs. php-fpm and the queue worker
wrote to supervisord child logfiles under /tmp, so application errors and
failed queue jobs were invisible to 'docker logs' and any log collector.
Route stdout/stderr of every supervised program to fd 1.

Also on the queue worker:
- stopwaitsecs=3600 so in-flight jobs get to finish on shutdown instead
  of being SIGKILLed after supervisord's 10 second default (per the
  Laravel deployment docs; pair with a compose stop_grace_period if jobs
  routinely run long)
- --max-time=3600 so the long-lived worker recycles itself hourly as a
  memory-leak guard; supervisord restarts it automatically
2026-07-15 20:39:45 -04:00
2 changed files with 61 additions and 85 deletions

View File

@@ -13,92 +13,57 @@ return new class extends Migration
*/
public function up(): void
{
if (!Schema::hasTable('backup_hosts')) {
Schema::create('backup_hosts', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('schema');
$table->json('configuration')->nullable();
$table->timestamps();
});
}
if (!Schema::hasTable('backup_host_node')) {
Schema::create('backup_host_node', function (Blueprint $table) {
$table->unsignedInteger('node_id');
$table->foreign('node_id')->references('id')->on('nodes')->cascadeOnDelete();
$table->unsignedInteger('backup_host_id');
$table->foreign('backup_host_id')->references('id')->on('backup_hosts')->cascadeOnDelete();
$table->timestamps();
$table->unique(['node_id']);
});
}
$backupHost = BackupHost::first();
if (!$backupHost) {
$oldDriver = env('APP_BACKUP_DRIVER', 'wings');
$oldConfiguration = null;
if ($oldDriver === 's3') {
$oldConfiguration = [
'region' => env('AWS_DEFAULT_REGION'),
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'bucket' => env('AWS_BACKUPS_BUCKET'),
'prefix' => env('AWS_BACKUPS_BUCKET', ''),
'endpoint' => env('AWS_ENDPOINT'),
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
'use_accelerate_endpoint' => env('AWS_BACKUPS_USE_ACCELERATE', false),
'storage_class' => env('AWS_BACKUPS_STORAGE_CLASS'),
];
}
$backupHost = BackupHost::create([
'name' => $oldDriver === 's3' ? 'Remote' : 'Local',
'schema' => $oldDriver,
'configuration' => $oldConfiguration,
]);
}
// The column must start out nullable: adding it NOT NULL would give existing
// rows a value of 0, which the foreign key below would reject.
if (!Schema::hasColumn('backups', 'backup_host_id')) {
$hasDiskColumn = Schema::hasColumn('backups', 'disk');
Schema::table('backups', function (Blueprint $table) use ($hasDiskColumn) {
$column = $table->unsignedInteger('backup_host_id')->nullable();
// The disk column may already be gone on installs that were repaired by hand.
if ($hasDiskColumn) {
$column->after('disk');
}
});
}
DB::table('backups')
->whereNull('backup_host_id')
->orWhere('backup_host_id', 0)
->update(['backup_host_id' => $backupHost->id]);
Schema::table('backups', function (Blueprint $table) {
$table->unsignedInteger('backup_host_id')->nullable(false)->change();
Schema::create('backup_hosts', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('schema');
$table->json('configuration')->nullable();
$table->timestamps();
});
$foreignKeys = array_column(Schema::getForeignKeys('backups'), 'name');
if (!in_array('backups_backup_host_id_foreign', $foreignKeys)) {
Schema::table('backups', function (Blueprint $table) {
$table->foreign('backup_host_id')->references('id')->on('backup_hosts');
});
Schema::create('backup_host_node', function (Blueprint $table) {
$table->unsignedInteger('node_id');
$table->foreign('node_id')->references('id')->on('nodes')->cascadeOnDelete();
$table->unsignedInteger('backup_host_id');
$table->foreign('backup_host_id')->references('id')->on('backup_hosts')->cascadeOnDelete();
$table->timestamps();
$table->unique(['node_id']);
});
Schema::table('backups', function (Blueprint $table) {
$table->unsignedInteger('backup_host_id')->after('disk');
$table->foreign('backup_host_id')->references('id')->on('backup_hosts');
$table->dropColumn('disk');
});
$oldDriver = env('APP_BACKUP_DRIVER', 'wings');
$oldConfiguration = null;
if ($oldDriver === 's3') {
$oldConfiguration = [
'region' => env('AWS_DEFAULT_REGION'),
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'bucket' => env('AWS_BACKUPS_BUCKET'),
'prefix' => env('AWS_BACKUPS_BUCKET', ''),
'endpoint' => env('AWS_ENDPOINT'),
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
'use_accelerate_endpoint' => env('AWS_BACKUPS_USE_ACCELERATE', false),
'storage_class' => env('AWS_BACKUPS_STORAGE_CLASS'),
];
}
if (Schema::hasColumn('backups', 'disk')) {
Schema::table('backups', function (Blueprint $table) {
$table->dropColumn('disk');
});
}
$backupHost = BackupHost::create([
'name' => $oldDriver === 's3' ? 'Remote' : 'Local',
'schema' => $oldDriver,
'configuration' => $oldConfiguration,
]);
DB::table('backups')->update(['backup_host_id' => $backupHost->id]);
}
/**
@@ -113,8 +78,8 @@ return new class extends Migration
$table->dropColumn('backup_host_id');
});
Schema::dropIfExists('backup_host_node');
Schema::dropIfExists('backup_hosts');
Schema::dropIfExists('backup_host_node');
}
};

View File

@@ -25,11 +25,20 @@ password=dummy
command=/usr/local/sbin/php-fpm -F
autostart=true
autorestart=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true
[program:queue-worker]
command=/usr/local/bin/php /var/www/html/artisan queue:work --tries=3
command=/usr/local/bin/php /var/www/html/artisan queue:work --tries=3 --max-time=3600
autostart=true
autorestart=true
; give running jobs time to finish on shutdown instead of killing them
; after supervisord's 10 second default
stopwaitsecs=3600
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true
[program:caddy]
command=caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
@@ -44,4 +53,6 @@ redirect_stderr=true
command=supercronic -overlapping /etc/crontabs/crontab
autostart=true
autorestart=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true