├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md └── nixpacks.toml /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | node_modules/ 3 | npm-debug.log 4 | yarn-error.log 5 | 6 | # Laravel 4 specific 7 | bootstrap/compiled.php 8 | app/storage/ 9 | 10 | # Laravel 5 & Lumen specific 11 | public/storage 12 | public/hot 13 | 14 | # Laravel 5 & Lumen specific with changed public path 15 | public_html/storage 16 | public_html/hot 17 | 18 | storage/*.key 19 | .env 20 | Homestead.yaml 21 | Homestead.json 22 | /.vagrant 23 | .phpunit.result.cache 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 marcfowler 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # coolify-php-nginx-laravel 2 | 3 | This is an example repository containing the full code I've used to [deploy PHP, nginx, and Laravel queues](https://frontier.sh/posts/20240510-phpfpm-nginx/) on [Coolify](https://coolify.io/). 4 | 5 | ## How does it work? 6 | 7 | The `./nixpacks.toml` file sets up the following static files: 8 | 9 | - our `start.sh` script, which starts up supervisord, which in turn starts all of its workers and keeps them up 10 | - our `supervisord.conf` file that configures supervisord 11 | - `worker-X.conf` files used to tell supervisord how to run PHP, nginx, and the default Laravel queue 12 | - the `php-fpm.conf` file used for PHP-FPM's configuration 13 | - our `nginx-template.conf` file used for nginx's configuration 14 | 15 | You don't need to include these files yourself; they're written as nixpacks builds the image for you. 16 | 17 | Those files get placed into the `./assets` directory on build, and the commands in the `[phases.build]` section move those files and use them appropriately. 18 | 19 | You should be able to just drop this file into your repository, set Coolify's "Build pack" option to "Nixpacks" (the default), and configure the other settings to pull from your Github repo etc. 20 | 21 | You can make any adjustments to the supervisord, PHP-FPM or nginx config as needed and redeploy for them to take effect. For example, you might want to reduce the number of Laravel queue workers, or adjust how many pools PHP-FPM uses. -------------------------------------------------------------------------------- /nixpacks.toml: -------------------------------------------------------------------------------- 1 | [phases.setup] 2 | nixPkgs = ["...", "python311Packages.supervisor"] 3 | 4 | [phases.build] 5 | cmds = [ 6 | "mkdir -p /etc/supervisor/conf.d/", 7 | "cp /assets/worker-*.conf /etc/supervisor/conf.d/", 8 | "cp /assets/supervisord.conf /etc/supervisord.conf", 9 | "chmod +x /assets/start.sh", 10 | "..." 11 | ] 12 | 13 | [start] 14 | cmd = '/assets/start.sh' 15 | 16 | [staticAssets] 17 | "start.sh" = ''' 18 | #!/bin/bash 19 | 20 | # Transform the nginx configuration 21 | node /assets/scripts/prestart.mjs /assets/nginx.template.conf /etc/nginx.conf 22 | 23 | # Start supervisor 24 | supervisord -c /etc/supervisord.conf -n 25 | ''' 26 | 27 | "supervisord.conf" = ''' 28 | [unix_http_server] 29 | file=/assets/supervisor.sock 30 | 31 | [supervisord] 32 | logfile=/var/log/supervisord.log 33 | logfile_maxbytes=50MB 34 | logfile_backups=10 35 | loglevel=info 36 | pidfile=/assets/supervisord.pid 37 | nodaemon=false 38 | silent=false 39 | minfds=1024 40 | minprocs=200 41 | 42 | [rpcinterface:supervisor] 43 | supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface 44 | 45 | [supervisorctl] 46 | serverurl=unix:///assets/supervisor.sock 47 | 48 | [include] 49 | files = /etc/supervisor/conf.d/*.conf 50 | ''' 51 | 52 | "worker-nginx.conf" = ''' 53 | [program:worker-nginx] 54 | process_name=%(program_name)s_%(process_num)02d 55 | command=nginx -c /etc/nginx.conf 56 | autostart=true 57 | autorestart=true 58 | stdout_logfile=/var/log/worker-nginx.log 59 | stderr_logfile=/var/log/worker-nginx.log 60 | ''' 61 | 62 | "worker-phpfpm.conf" = ''' 63 | [program:worker-phpfpm] 64 | process_name=%(program_name)s_%(process_num)02d 65 | command=php-fpm -y /assets/php-fpm.conf -F 66 | autostart=true 67 | autorestart=true 68 | stdout_logfile=/var/log/worker-phpfpm.log 69 | stderr_logfile=/var/log/worker-phpfpm.log 70 | ''' 71 | 72 | "worker-laravel.conf" = ''' 73 | [program:worker-laravel] 74 | process_name=%(program_name)s_%(process_num)02d 75 | command=bash -c 'exec php /app/artisan queue:work --sleep=3 --tries=3 --max-time=3600' 76 | autostart=true 77 | autorestart=true 78 | stopasgroup=true 79 | killasgroup=true 80 | numprocs=12 81 | startsecs=0 82 | stopwaitsecs=3600 83 | stdout_logfile=/var/log/worker-laravel.log 84 | stderr_logfile=/var/log/worker-laravel.log 85 | ''' 86 | 87 | "php-fpm.conf" = ''' 88 | [www] 89 | listen = 127.0.0.1:9000 90 | user = www-data 91 | group = www-data 92 | listen.owner = www-data 93 | listen.group = www-data 94 | pm = dynamic 95 | pm.max_children = 50 96 | pm.min_spare_servers = 4 97 | pm.max_spare_servers = 32 98 | pm.start_servers = 18 99 | clear_env = no 100 | ''' 101 | 102 | "nginx.template.conf" = ''' 103 | user www-data www-data; 104 | worker_processes 5; 105 | daemon off; 106 | 107 | worker_rlimit_nofile 8192; 108 | 109 | events { 110 | worker_connections 4096; # Default: 1024 111 | } 112 | 113 | http { 114 | include $!{nginx}/conf/mime.types; 115 | index index.html index.htm index.php; 116 | 117 | default_type application/octet-stream; 118 | log_format main '$remote_addr - $remote_user [$time_local] $status ' 119 | '"$request" $body_bytes_sent "$http_referer" ' 120 | '"$http_user_agent" "$http_x_forwarded_for"'; 121 | access_log /var/log/nginx-access.log; 122 | error_log /var/log/nginx-error.log; 123 | sendfile on; 124 | tcp_nopush on; 125 | server_names_hash_bucket_size 128; # this seems to be required for some vhosts 126 | 127 | server { 128 | listen ${PORT}; 129 | listen [::]:${PORT}; 130 | server_name localhost; 131 | 132 | $if(NIXPACKS_PHP_ROOT_DIR) ( 133 | root ${NIXPACKS_PHP_ROOT_DIR}; 134 | ) else ( 135 | root /app; 136 | ) 137 | 138 | add_header X-Content-Type-Options "nosniff"; 139 | 140 | client_max_body_size 35M; 141 | 142 | index index.php; 143 | 144 | charset utf-8; 145 | 146 | $if(IS_LARAVEL) ( 147 | location / { 148 | try_files $uri $uri/ /index.php?$query_string; 149 | } 150 | ) else () 151 | 152 | $if(NIXPACKS_PHP_FALLBACK_PATH) ( 153 | location / { 154 | try_files $uri $uri/ ${NIXPACKS_PHP_FALLBACK_PATH}?$query_string; 155 | } 156 | ) else () 157 | 158 | location = /favicon.ico { access_log off; log_not_found off; } 159 | location = /robots.txt { access_log off; log_not_found off; } 160 | 161 | $if(IS_LARAVEL) ( 162 | error_page 404 /index.php; 163 | ) else () 164 | 165 | location ~ \.php$ { 166 | fastcgi_buffer_size 8k; 167 | fastcgi_pass 127.0.0.1:9000; 168 | fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; 169 | include $!{nginx}/conf/fastcgi_params; 170 | include $!{nginx}/conf/fastcgi.conf; 171 | 172 | fastcgi_param PHP_VALUE "upload_max_filesize=30M \n post_max_size=35M"; 173 | } 174 | 175 | location ~ /\.(?!well-known).* { 176 | deny all; 177 | } 178 | } 179 | } 180 | ''' 181 | --------------------------------------------------------------------------------