Export and Containerize
What You Will Learn
- What files a Godot HTML5 export produces and why each one matters
- How to write a Dockerfile that correctly serves a Godot web export
- How to build a multi-architecture image and push it to ECR
Step 1: Export from Godot
You already know how to do this, so here is the quick version:
- Open your project in Godot
- Go to Project > Export
- Select Web (or add it if it is not there yet)
- Set the export path to a folder called
export/inside your project - Click Export Project (uncheck "Export With Debug" for a smaller build)
What the Export Produces
After exporting, your export/ folder will contain these files:
| File | Purpose |
|---|---|
index.html |
The HTML page that loads the game engine |
index.js |
JavaScript glue code that initializes the WebAssembly runtime |
index.wasm |
The compiled game engine (WebAssembly binary) |
index.pck |
Your game's packed resources (scenes, scripts, assets) |
index.png |
The default favicon/icon |
index.apple-touch-icon.png |
iOS home screen icon |
These are all static files. To deploy them, you just need a web server that can serve them with the correct headers.
Step 2: The Dockerfile
Godot HTML5 exports need specific HTTP headers to work. The game engine uses SharedArrayBuffer, which browsers only allow when the server sends the right cross-origin isolation headers. Without them, your game will fail to load with a cryptic error in the console.
Here is the complete Dockerfile:
# Use nginx:alpine as the base image
# Alpine keeps the image small (~40MB vs ~140MB for full nginx)
FROM nginx:alpine
# Remove the default nginx welcome page
RUN rm -rf /usr/share/nginx/html/*
# Copy the Godot export files into the nginx web root
COPY export/ /usr/share/nginx/html/
# Create a custom nginx config that:
# 1. Sets correct MIME types for .wasm and .pck files
# 2. Adds cross-origin isolation headers required by SharedArrayBuffer
RUN cat > /etc/nginx/conf.d/default.conf << 'EOF'
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# Correct MIME types for Godot export files
types {
application/wasm wasm;
application/octet-stream pck;
}
# Cross-origin isolation headers required for SharedArrayBuffer
# Without these, the Godot engine will refuse to start
add_header Cross-Origin-Opener-Policy "same-origin" always;
add_header Cross-Origin-Embedder-Policy "require-corp" always;
location / {
try_files $uri $uri/ =404;
}
}
EOF
# Expose port 80 (nginx default)
EXPOSE 80
# nginx:alpine already has CMD ["nginx", "-g", "daemon off;"]
# so we do not need to specify it again
Save this file as Dockerfile in the root of your Godot project (next to the export/ folder).
Why These Headers Matter
| Header | What It Does |
|---|---|
Cross-Origin-Opener-Policy: same-origin |
Isolates the browsing context so SharedArrayBuffer is allowed |
Cross-Origin-Embedder-Policy: require-corp |
Ensures all loaded resources are explicitly CORS-opted-in |
Without both headers, modern browsers (Chrome 92+, Firefox 79+) will block SharedArrayBuffer and your game will not load.
Step 3: Build the Multi-Architecture Image
The Junovy cluster runs exclusively on ARM64 nodes. However, you likely develop on an x86 (amd64) machine. Building multi-architecture images ensures your containers work both locally and on the cluster. Docker Buildx handles this for you.
Set Up Buildx (One-Time)
# Create a new builder that supports multi-platform builds
docker buildx create --name junovy-builder --use
# Verify it supports both platforms
docker buildx inspect --bootstrap
Authenticate with ECR
# Log in to the Junovy ECR registry
aws ecr get-login-password --region eu-central-1 | \
docker login --username AWS --password-stdin \
<account-id>.dkr.ecr.eu-central-1.amazonaws.com
Replace <account-id> with the actual AWS account ID (ask your team lead if you do not have it).
Build and Push
# Build for both architectures and push directly to ECR
# --platform: target architectures (required by Junovy)
# --tag: the full ECR image path with version tag
# --push: build and push in one step (multi-arch can't use --load)
docker buildx build \
--platform linux/amd64,linux/arm64 \
--tag <account-id>.dkr.ecr.eu-central-1.amazonaws.com/godot-demo:v1.0.0 \
--push \
.
Verify the Push
# List the tags in the ECR repository to confirm it arrived
aws ecr describe-images \
--repository-name godot-demo \
--region eu-central-1 \
--query 'imageDetails[*].imageTags' \
--output table
You should see v1.0.0 in the output.
Your Project Structure So Far
At this point, your local Godot project directory should look like this:
my-godot-game/
project.godot # Your Godot project file
Dockerfile # The Dockerfile you just created
export/ # The HTML5 export output
index.html
index.js
index.wasm
index.pck
index.png
index.apple-touch-icon.png
scenes/ # Your game scenes (not deployed)
scripts/ # Your game scripts (not deployed)
Only the export/ folder goes into the container. Your source code stays local.
Key Takeaways
- Godot HTML5 exports produce static files (HTML, JS, WASM, PCK) that need a web server
- The Dockerfile uses
nginx:alpineand adds headers forSharedArrayBuffersupport - Without cross-origin isolation headers, the game will not load in modern browsers
- The cluster runs ARM64 nodes, so images must include
linux/arm64(pluslinux/amd64for local testing) - The image is pushed to the Junovy AWS ECR registry in
eu-central-1
What Is Next
Next up: Create the Tenant where you will set up the namespace and directory structure in the Flux repo so the cluster knows where to put your game.