Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QUESTION] Is it possible to run multiple FrankenPHP containers at the same time? #670

Open
Natetronn opened this issue Mar 17, 2024 · 7 comments

Comments

@Natetronn
Copy link

Hello!

Does anyone know how to run multiple containers of FrankenPHP at the same time?

I'd like to use it for local dev environment instead of XAMPP (WAMP/MAMP et al.) and this for a bunch of different projects; that all live in different directories and will all use different containers (if possible.)

If I'm not mistaken, after some research, Caddy requires the host's ports 80 and 443 for ACME Cert reasons. So changing those to, say, 8080:80 and 4443:443 doesn't work. (?) I'm using custom testing domains that I set in /etc/hosts (host side) and then in the Caddyfile. That works fine, as long as I keep the ports as is, but if I change the ports to allow for multiple containers it no longer works.

Aside: I tried, but don't know how to run php_server and reverse_proxy localhost:4443 at the same time or if it's even possible, thinking that might lead to a possible solution.

Turning off and on containers isn't the end of the world, but it's less than ideal. With that said, is there a better way to manage a bunch of projects with FrankenPHP all running at the same time?

Appreciate any suggestions, thank you!

@sneycampos
Copy link
Contributor

maybe changing https_port in Caddyfile? https://caddyserver.com/docs/caddyfile/options#https-port

@StephenMiracle
Copy link
Sponsor Contributor

auto_https disable_redirects can be set as a global option in Caddy. Then you can set the server_name to something like :8095

@Natetronn
Copy link
Author

Natetronn commented Mar 21, 2024

Thanks for the replies!

I wasn't able to figure anything further using those suggestions, however, I got it working on Linux by setting the IP address in /etc/hosts using the container's IP address instead of 127.0.0.1, as I had before.

On Windows with Docker Desktop (in WSL2 mode) I couldn't get it working with changed host ports unless I used the port in the url too. So one could add the various ports to that url or just use one container at a time and stick to the default host ports like so:

   ports:
     - 80:80
     - 443:443
     - 443:443/udp

If anyone wants to play around here's what I have so far:

docker-compose.yml

version: "3.8"

services:
  frankenphp:
    container_name: mydomain
    # image: dunglas/frankenphp
    build: .
    restart: unless-stopped
    ports:
      - 8081:80
      - 4434:443
      - 4434:443/udp
    volumes:
      - ./public:/app/public
      - ./caddy/data:/data
      - ./caddy/config:/config
      - ./caddy/certs:/certs
      - ./caddy/log:/var/log
      - ./caddy/Caddyfile:/etc/caddy/Caddyfile
    environment:
      # sets the domain in Caddyfile - make sure to use the same domain when creating your self-signed certificates
      - DOMAIN=mydomain.loc

Dockerfile

FROM dunglas/frankenphp:latest-php8.3.4-alpine

# Add additional PHP extensions or Composer
# https://frankenphp.dev/docs/docker/#how-to-install-more-php-extensions
# https://github.com/mlocati/docker-php-extension-installer
# https://github.com/mlocati/docker-php-extension-installer?tab=readme-ov-file#supported-php-extensions
# https://github.com/mlocati/docker-php-extension-installer?tab=readme-ov-file#installing-composer
RUN install-php-extensions gd xdebug yaml

Caddyfile

{
	# Enable FrankenPHP
	frankenphp
	# Configure when the directive must be executed
	order php_server before file_server
}

{$DOMAIN} {
	# Enable logging; update as needed - https://caddyserver.com/docs/caddyfile/directives/log
	# log {
	# 	output file /var/log/access.log
	# }

	# tls internal works, but https cert won't be valid; use your own self-signed certs instead if you want https
	# tls internal 

	# Generate your own self-signed certificates using mkcert or similar - set the URL environment variable to the same domain name
	tls /certs/{$DOMAIN}.cert.pem /certs/{$DOMAIN}.key.pem

	# Serve static files
	root * /app/public

	# Enable compression (optional)
	encode zstd br gzip

	# Execute PHP (FrankenPHP) in the aboven root directory and serve assets
	php_server
}

/etc/hosts
172.XXX.X.X mydomain.loc

or

c:\Windows\System32\drivers\etc\hosts
127.0.0.1 mydomain.loc

Note: you can find the IPAddress using docker inspect <Container ID or Name> | grep IPAddress or just docker inspect <Container ID or Name>. You can also find it in Portainer or Docker Desktop (I think under Inspect tab > network).

To create certs you can use one of these scripts:

create-certs.sh (requires mkcert and possibly nss)

#!/usr/bin/env bash

# Parameters

# Set DOMAIN variable to the value of the first positional parameter,
# or default to "localhost" if no parameter is provided
DOMAIN="${1:-localhost}"

# Set CERTS_DIR variable to the value of the second positional parameter,
# or default to "./certs" if no parameter is provided
CERTS_DIR="${2:-./certs}"

# File names
CERT_PEM_FILE="${CERTS_DIR}/${DOMAIN}.cert.pem"
KEY_PEM_FILE="${CERTS_DIR}/${DOMAIN}.key.pem"

# first ensure required executables exists:
if [[ `which mkcert` == "" ]] || [[ `nss-config nss --version` == "" ]]; then
    echo "Requires: mkcert & nss"
    echo
    echo "Run: sudo pacman -S mkcert nss"
    exit 1
fi

# finally install certificates
echo "-- Installing mkcert ..."
mkcert -install

mkdir -p ${CERTS_DIR}

echo "-- Creating and installing local SSL certificates for domain: ${DOMAIN} ..."
mkcert -cert-file ${CERT_PEM_FILE} -key-file ${KEY_PEM_FILE} "${DOMAIN}"

echo "-- Complete!"
echo
echo "- Now you can run: docker compose up -d"
echo "- Open browser to domain: https://${DOMAIN}"

create-certs.bat (requires choco and mkcert)

@echo off

REM parameters
SET DOMAIN=%1
IF "%DOMAIN%"=="" SET DOMAIN=localhost
SET CERTS_DIR=%2
IF "%CERTS_DIR%"=="" SET CERTS_DIR=.\certs
SET CERT_PEM_FILE=%CERTS_DIR%\%DOMAIN%.cert.pem
SET KEY_PEM_FILE=%CERTS_DIR%\%DOMAIN%.key.pem

REM Check if mkcert is installed
where mkcert >nul 2>nul
IF %ERRORLEVEL% NEQ 0 (
    echo Requires: mkcert
    echo.
    echo Run: choco install mkcert
    exit /b 1
)

REM Finally install certificates
echo -- Installing mkcert ...
mkcert -install

mkdir %CERTS_DIR%

echo -- Creating and installing local SSL certificates for domain: %DOMAIN% ...
mkcert -cert-file %CERT_PEM_FILE% -key-file %KEY_PEM_FILE% "%DOMAIN%"

echo -- Complete!
echo.
echo - Now you can run: docker compose up -d
echo - Open browser to domain: https://%DOMAIN%

Either script will take a domain (mydomain.loc for example) for the first parameter and an output path for the second.

@StephenMiracle
Copy link
Sponsor Contributor

Interesting. I use Windows Docker with WSL. But I typically just map different ports to different apps that I want to run.

App 1:
8090:80

App 2:
8091:80

etc then I just run localhost:8090 or localhost:8091

Is there a reason why you need certs locally? Doesn't seem like you would need to manage a custom caddyfile if you could avoid the cert requirement for local dev. Every place is unique but I try to avoid local cert management on windows whenever possible.

@Asociateone
Copy link

@Natetronn I use DDEV instead of XAMPP/MAMP that seems allot simpeler just for a dev environment.

https://ddev.com/

But it is also possible with caddy reverse proxy and your host file i think.

With a reverse proxy from caddy it uses a internal port of the container not an exposed port in the host machine:

so in the Caddyfile from your caddy network there would be something like this:

url.com {
reverse_proxy dockercontainername:80
}

for the frankenphp container make sure it is not creating an ssl certificate because your caddy network does that for you.

services:
php:
image: dunglas/frankenphp
restart: always
networks:
- caddy_caddy <- use the network that your caddy router is listening on
environment:
SERVER_NAME: :80

@Natetronn
Copy link
Author

Natetronn commented Mar 28, 2024

@Asociateone thanks for the suggestion!

I am familiar with ddev (and Devilbox et al.), but I wanted to move in a different direction; one that is a bit more minimal, where you only add the things you need, not a full blown system with opinions, where I won't use half the stuff included. Also, I want it more portable, where I can use the same or mostly the same setup locally and then also on the production side (like on DO or Linode etc.)

Aside: this reminds me, I maintain the Docksal AUR package even though I don't personally use it. I should check on that.

I already had a "normal" docker php / caddy setup working that is similar to frankenphp, but frankenphp was slightly less complicated (or so I thought) in that it was all wrapped up in one container, so I was trying to move to that for the sake of simplicity and others. I even had docker php extension installer working, which is one of the pros of frankenphp.

With that said, not sure it would make sense at that point to even use frankenphp (if I was moving to a standalone caddy). But I'll think about it...and now that I'm thinking of it, frankenphp does have some other features to consider, like performance, for example, so maybe still something to consider.

@StephenMiracle sorry for the late response. Yeah, for sure...I don't really NEED local certs, but it was more of a nice to have kind of WANT; trying to get as close to the real thing as possible kind of thing.

In regards to Windows, yeah, I use Linux myself lol. I do, however, have Windows (dual boot) setup for testing this kind of stuff. And I have some friends and colleagues who use Windows and my attempt to resolve this was really for their sake. Everything was working as I wanted on my side, but not on the Windows side. And, well, sometimes I get going down a path and just don't know when to give up, I guess lol

And on that note, I'll leave this open for now, in case anyone wants to continue the conversation, but this is mostly a non-issue at this point, so don't feel the need to keep trying to solve this; unless you want to, of course. I've mostly moved on, though.

Anyway, I really appreciate everyone suggestions and insights!

@sneycampos
Copy link
Contributor

sneycampos commented Apr 18, 2024

Anyway, I really appreciate everyone suggestions and insights!

Did you found any solution? what you think about this:

docker-compose.yml

services:

    php:
        working_dir: /app
        environment:
            - ENV REQUEST_MAX_EXECUTION_TIME=0
            - HTTP_PORT=8080
            - HTTPS_PORT=8443
            - SERVER_NAME=localhost:${HTTP_PORT}
        build:
            context: .
            dockerfile: Dockerfile
        volumes:
            - ./:/app
            - ./Caddyfile:/etc/caddy/Caddyfile
        ports:
            - 8080:8080
            - 8443:8443

    php-2:
        working_dir: /app
        environment:
            - ENV REQUEST_MAX_EXECUTION_TIME=0
            - HTTP_PORT=8081
            - HTTPS_PORT=8444
            - SERVER_NAME=localhost:${HTTP_PORT}
        build:
            context: .
            dockerfile: Dockerfile
        volumes:
            - ./:/app
            - ./Caddyfile:/etc/caddy/Caddyfile
        ports:
            - 8081:8081
            - 8444:8444

Dockerfile

FROM dunglas/frankenphp:latest-alpine

RUN apk add --no-cache git

RUN install-php-extensions \
	pdo_mysql \
	gd \
	intl \
	zip

COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

ARG USER=application
ARG UID=1000

RUN addgroup -g ${UID} ${USER}; \
    adduser -D -u ${UID} -G ${USER} ${USER}; \
    chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy;

USER ${USER}

ARG HTTP_PORT=8080
ARG HTTPS_PORT=8443

EXPOSE ${HTTP_PORT} ${HTTPS_PORT}

ENV HTTP_PORT=${HTTP_PORT}
ENV HTTPS_PORT=${HTTPS_PORT}

Caddyfile

{
	{$CADDY_GLOBAL_OPTIONS}

	http_port {$HTTP_PORT:80}
	https_port {$HTTPS_PORT:443}

	frankenphp {
		{$FRANKENPHP_CONFIG}
	}

	# https://caddyserver.com/docs/caddyfile/directives#sorting-algorithm
	order mercure after encode
	order vulcain after reverse_proxy
	order php_server before file_server
	order php before file_server
}

{$CADDY_EXTRA_CONFIG}

{$SERVER_NAME:localhost} {
	root * public/
	encode zstd br gzip

	{$CADDY_SERVER_EXTRA_DIRECTIVES}

	php_server
}

Using this way you can have multiple https://localhost (using different ports)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants