1
0
This commit is contained in:
Thomas Belway 2026-02-13 00:03:25 -05:00
parent b88b906a39
commit d98a8cc4c9
3 changed files with 175 additions and 60 deletions

View File

@ -1,34 +1,55 @@
# Caddy recommended way to build the custom image
FROM registry.belway.me/belwayhome/almalinux9-minimal:latest
# FoundryVTT Container Image
# Built on Red Hat UBI10 Minimal for security and stability
FROM registry.access.redhat.com/ubi10/ubi-minimal:latest
ARG foundry_url
ARG nodejs_version
ENV foundry_url $foundry_url
ENV nodejs_version $nodejs_version
# OCI Labels
LABEL org.opencontainers.image.title="FoundryVTT"
LABEL org.opencontainers.image.description="FoundryVTT Virtual Tabletop on Red Hat UBI10"
LABEL org.opencontainers.image.source="https://gitea.belway.me/Public/foundryvtt_container"
LABEL org.opencontainers.image.vendor="BelwayHome"
# Because pipeline is rootless
VOLUME /var/lib/containers
VOLUME /home/podman/.local/share/containers
ARG FOUNDRY_URL
ARG FOUNDRY_VERSION=latest
ARG NODEJS_VERSION=22
RUN microdnf update --assumeyes \
&& microdnf --assumeyes install \
openssl-devel \
# Install dependencies and Node.js via dnf module
RUN microdnf update -y --nodocs && \
microdnf install -y --nodocs --setopt=install_weak_deps=0 \
nodejs \
npm \
openssl \
tar \
unzip \
wget \
&& rm -rf /var/cache/yum
shadow-utils && \
microdnf clean all && \
rm -rf /var/cache/yum
RUN curl --silent --location https://rpm.nodesource.com/setup_${nodejs_version}.x | bash - \
&& microdnf --assumeyes install \
nodejs \
&& rm -rf /var/cache/yum
# Create non-root user
RUN groupadd -r foundry && \
useradd -r -g foundry -d /foundryvtt -s /sbin/nologin foundry
WORKDIR /foundryvtt/foundryvtt
RUN wget -O foundryvtt.zip ${foundry_url} && unzip foundryvtt.zip && rm foundryvtt.zip
# Download and extract FoundryVTT
WORKDIR /foundryvtt/app
RUN wget -q -O foundryvtt.zip "${FOUNDRY_URL}" && \
unzip -q foundryvtt.zip && \
rm foundryvtt.zip && \
chown -R foundry:foundry /foundryvtt
# Create data directory
RUN mkdir -p /foundryvtt/data && \
chown -R foundry:foundry /foundryvtt/data
WORKDIR /foundryvtt
VOLUME /foundryvtt/foundrydata
# Set ownership and switch to non-root user
USER foundry
VOLUME /foundryvtt/data
EXPOSE 30000
ENTRYPOINT /usr/bin/node /foundryvtt/foundryvtt/resources/app/main.js --dataPath=/foundryvtt/foundrydata
# Version label (set at build time)
LABEL org.opencontainers.image.version="${FOUNDRY_VERSION}"
# Use exec form for proper signal handling
ENTRYPOINT ["/usr/bin/node", "/foundryvtt/app/resources/app/main.js", "--dataPath=/foundryvtt/data"]

128
README.md
View File

@ -1,32 +1,75 @@
# foundryvtt_container
If you don't want to build and host your own container image feel free to skip ahead and leverage the publicly hosted container. Head over to "Running the Container" if this is the better use-case for you.
# FoundryVTT Container
## Builidng the Container
Just run the script with the URL and Version declared as follows:
A containerized FoundryVTT Virtual Tabletop built on Red Hat UBI10 Minimal.
## Features
- Red Hat UBI10 Minimal base image
- Runs as non-root user for security
- OCI-compliant image labels
- Automated CI/CD builds
## Quick Start
Pull the latest image:
```bash
./build.sh -U $FOUNDRY_URL -I -v $VERSION
podman pull registry.belway.me/public/foundryvtt:latest
```
## Running the Container
When setting hostname, make sure you use the hostname of the baremetal server or VM that you are running the container on. Otherwise you will need to accept the license at each initialization.
Assuming you are using rootless like I am, you'll need to have created a rootless user and enabled lingering.
I'm using quadlets, so here's an example quadlet:
Run with persistent data:
```bash
# in your rootless podman container directory, ie: /etc/containers/systemd/users/${USER_UID}/
# foundryvtt.container
podman run -d \
--name foundryvtt \
-p 30000:30000 \
-v /path/to/data:/foundryvtt/data:Z \
--hostname $(hostname) \
registry.belway.me/public/foundryvtt:latest
```
> **Note:** Set `--hostname` to your server's hostname to avoid re-accepting the license on each restart.
## Building the Container
### Prerequisites
- A valid FoundryVTT license
- The timed download URL from your FoundryVTT account (Purchased Licenses → Linux/NodeJS)
### Manual Build
```bash
./build.sh -U "$FOUNDRY_URL" -I -v "$VERSION"
```
### CI/CD
The repository includes a Gitea Actions workflow that automatically builds when:
- Changes are pushed to `main` branch
- Manually triggered via workflow dispatch
Required secrets:
- `FOUNDRY_URL` - Timed download URL from FoundryVTT
- `BELWAY_REGISTRY_USER` / `BELWAY_REGISTRY_TOKEN` - Registry credentials
## Running with Quadlets (Recommended)
Create a quadlet file at `~/.config/containers/systemd/foundryvtt.container`:
```ini
[Unit]
Description=FoundryVTT Custom Container
Requires=var-mnt-data01.mount
Description=FoundryVTT Virtual Tabletop
After=network-online.target
[Container]
AutoUpdate=registry
HostName=${VM_HOSTNAME}
Image=registry.belway.me/public/foundryvtt_container:latest
Label=registry
Network=slirp4netns:port_handler=slirp4netns
Image=registry.belway.me/public/foundryvtt:latest
HostName=your-server-hostname
PublishPort=30000:30000
Timezone=America/Montreal
Volume=${YOUR_DATA_DIRECTORY_HERE}:/foundryvtt/foundrydata:z
Volume=/path/to/your/data:/foundryvtt/data:Z
# Healthcheck at runtime (OCI images don't include healthcheck)
HealthCmd=curl -f http://localhost:30000 || exit 1
HealthInterval=30s
HealthTimeout=10s
HealthStartPeriod=60s
HealthRetries=3
[Service]
Restart=always
@ -35,8 +78,55 @@ Restart=always
WantedBy=multi-user.target default.target
```
Then as the user, run:
Then start the service:
```bash
systemctl --user daemon-reload
systemctl --user start foundryvtt
systemctl --user enable foundryvtt
```
## Healthcheck Options
Since OCI-compliant images don't include healthchecks, add them at runtime:
### With podman run:
```bash
podman run -d \
--name foundryvtt \
--health-cmd="curl -f http://localhost:30000 || exit 1" \
--health-interval=30s \
--health-timeout=10s \
--health-start-period=60s \
-p 30000:30000 \
-v /path/to/data:/foundryvtt/data:Z \
registry.belway.me/public/foundryvtt:latest
```
### With Quadlet:
Add to `[Container]` section:
```ini
HealthCmd=curl -f http://localhost:30000 || exit 1
HealthInterval=30s
HealthTimeout=10s
HealthStartPeriod=60s
```
## Volume Mounts
| Container Path | Purpose |
|---------------|---------|
| `/foundryvtt/data` | Persistent data (worlds, systems, modules) |
## Ports
| Port | Protocol | Purpose |
|------|----------|---------|
| 30000 | TCP | Web interface |
## Environment Variables
FoundryVTT supports various command-line options that can be passed as arguments. See the [official documentation](https://foundryvtt.com/article/configuration/) for details.
## License
See [LICENSE](LICENSE) file.

View File

@ -12,7 +12,7 @@
####################################################################
# General Variables
REGISTRY="registry.belway.me"
REGISTRY_NAMESPACE="public/foundryvtt_container"
REGISTRY_NAMESPACE="public/foundryvtt"
###############################################################################################################################
# Functions
@ -24,18 +24,17 @@ cat << EOF
usage: ${SCRIPT_NAME} [options]
This script exports data from one database to be compared with data from another database
This script builds the FoundryVTT container and pushes to the registry.
OPTIONS:
-D Enable DEBUG mode
-f Custom Containerfile with absolute or relative directory
-F Format of containerfile
-F Format of containerfile (default: oci)
-h Show this message
-I Initialize the registry
-n Node version
-R For rebuild the image.
-S Scheduled build, should only be used by automation (gitea actions).
-U Foundry URL for downloading the version
-R Rebuild the image
-S Scheduled build, should only be used by automation (gitea actions)
-U Foundry URL for downloading the version (required)
-v Version to build, defaults to 'latest'
EOF
exit 1
@ -59,9 +58,6 @@ get_parameters() {
"I")
INITIALIZE="TRUE"
;;
"n")
NODEJS_VERSION="$OPTARG"
;;
"R")
OPT_REBUILD="TRUE"
;;
@ -71,7 +67,7 @@ get_parameters() {
"U")
FOUNDRY_URL="$OPTARG"
if [[ "$FOUNDRY_URL" != *"verify"* ]]; then
echo "Not a valid foundry URL, please use the TIMED URL option when at the Purchased LIcenses page on foundry website in your account."
echo "Not a valid foundry URL, please use the TIMED URL option when at the Purchased Licenses page on foundry website in your account."
exit 1
fi
;;
@ -104,7 +100,7 @@ get_parameters() {
fi
if [[ -z $OPT_FORMAT_TYPE ]]; then
OPT_FORMAT="--format docker"
OPT_FORMAT="--format oci"
elif [[ $OPT_FORMAT_TYPE == "podman" ]]; then
OPT_FORMAT=""
else
@ -133,10 +129,6 @@ get_parameters() {
OPT_REBUILD="FALSE"
fi
if [[ -z $NODEJS_VERSION ]]; then
NODEJS_VERSION=$(curl https://foundryvtt.com/article/installation/ | grep nodesource | grep rpm | tr -d -c 0-9)
fi
TAG="$OPT_VERSION"
}
@ -149,7 +141,11 @@ container_initialize() {
fi
echo "Initializing container build..."
podman login ${REGISTRY}
podman build -f ${OPT_CONTAINERFILE} -t ${REGISTRY}/${REGISTRY_NAMESPACE}:${TAG} -t ${REGISTRY}/${REGISTRY_NAMESPACE}:latest --build-arg foundry_url=${FOUNDRY_URL} --build-arg nodejs_version=${NODEJS_VERSION}
podman build ${OPT_FORMAT} -f ${OPT_CONTAINERFILE} \
-t ${REGISTRY}/${REGISTRY_NAMESPACE}:${TAG} \
-t ${REGISTRY}/${REGISTRY_NAMESPACE}:latest \
--build-arg FOUNDRY_URL="${FOUNDRY_URL}" \
--build-arg FOUNDRY_VERSION="${TAG}"
podman push ${REGISTRY}/${REGISTRY_NAMESPACE}:${TAG}
podman push ${REGISTRY}/${REGISTRY_NAMESPACE}:latest
podman logout ${REGISTRY}
@ -158,9 +154,13 @@ container_initialize() {
container_scheduled_build() {
echo "Scheduled build starting..."
if [[ "${CADDY_IMAGE_VERSION}" != "${STABLE[0]}" ]]; then
if [[ "${FOUNDRY_IMAGE_VERSION}" != "${STABLE[0]}" ]]; then
podman login ${REGISTRY}
podman build -f ${OPT_CONTAINERFILE} -t ${REGISTRY}/${REGISTRY_NAMESPACE}:${TAG} -t ${REGISTRY}/${REGISTRY_NAMESPACE}:latest --build-arg foundry_url=${FOUNDRY_URL}
podman build ${OPT_FORMAT} -f ${OPT_CONTAINERFILE} \
-t ${REGISTRY}/${REGISTRY_NAMESPACE}:${TAG} \
-t ${REGISTRY}/${REGISTRY_NAMESPACE}:latest \
--build-arg FOUNDRY_URL="${FOUNDRY_URL}" \
--build-arg FOUNDRY_VERSION="${TAG}"
podman push ${REGISTRY}/${REGISTRY_NAMESPACE}:${TAG}
podman push ${REGISTRY}/${REGISTRY_NAMESPACE}:latest
podman logout ${REGISTRY}
@ -174,7 +174,11 @@ container_scheduled_build() {
container_rebuild() {
echo "Rebuilding container..."
podman login ${REGISTRY}
podman build -f ${OPT_CONTAINERFILE} -t ${REGISTRY}/${REGISTRY_NAMESPACE}:${TAG} -t ${REGISTRY}/${REGISTRY_NAMESPACE}:latest --build-arg foundry_url=${FOUNDRY_URL}
podman build ${OPT_FORMAT} -f ${OPT_CONTAINERFILE} \
-t ${REGISTRY}/${REGISTRY_NAMESPACE}:${TAG} \
-t ${REGISTRY}/${REGISTRY_NAMESPACE}:latest \
--build-arg FOUNDRY_URL="${FOUNDRY_URL}" \
--build-arg FOUNDRY_VERSION="${TAG}"
podman push ${REGISTRY}/${REGISTRY_NAMESPACE}:${TAG}
podman push ${REGISTRY}/${REGISTRY_NAMESPACE}:latest
podman logout ${REGISTRY}