Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 92 additions & 14 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,58 @@
version: "3.8"

services:
monkeytype-frontend:
container_name: monkeytype-frontend
image: monkeytype/monkeytype-frontend:latest
ports:
- "${HTTP_PORT:-8080}:80"
monkeytype-proxy:
container_name: monkeytype-proxy
image: traefik:v3.7
restart: on-failure
depends_on:
monkeytype-backend:
condition: service_healthy
monkeytype-frontend:
condition: service_healthy
ports:
- "${HTTP_PORT:-80}:80"
- "${HTTPS_PORT:-443}:443"
#Dashboard disabled for security (uncomment only for debugging behind VPN)
#- "8080:8080"
networks:
- public
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./letsencrypt:/letsencrypt
command:
- "--providers.docker=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"

# enable for HTTPS
#- "--certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL}"
#- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
#- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
#- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
#- "--entrypoints.web.http.redirections.entrypoint.scheme=https"

# for debugging only
#- "--api.insecure=true"
#- "--log.level=DEBUG"

monkeytype-frontend:
container_name: monkeytype-frontend
image: monkeytype/monkeytype-frontend:latest
#uncomment if you want to expose the frontend server directly (not recommended)
#ports:
# - "${FRONTEND_PORT:-8080}:80"
restart: on-failure
networks:
- public
labels:
- "traefik.http.routers.frontend.rule=PathPrefix(`/`)"
- "traefik.http.routers.frontend.entrypoints=web,websecure"
- "traefik.http.routers.frontend.priority=25"
- "traefik.http.services.frontend.loadbalancer.server.port=80"
# enable for HTTPS
#- "traefik.http.routers.frontend.rule=Host(`${DOMAIN}`)"
#- "traefik.http.routers.frontend.tls=true"
#- "traefik.http.routers.frontend.tls.certresolver=letsencrypt"
#- "traefik.http.routers.frontend.tls.domains[0].main=${DOMAIN}"
env_file:
- path: ./.env
required: true
Expand All @@ -20,20 +63,30 @@ services:
- FIREBASE_STORAGEBUCKET=${FIREBASE_STORAGEBUCKET:-}
- FIREBASE_MESSAGINGSENDERID=${FIREBASE_MESSAGINGSENDERID:-}
- FIREBASE_APPID=${FIREBASE_APPID:-}
- MONKEYTYPE_BACKENDURL=${MONKEYTYPE_BACKENDURL}
- MONKEYTYPE_BACKENDURL=${BASE_URL:-http://localhost}/api
- RECAPTCHA_SITE_KEY=${RECAPTCHA_SITE_KEY:-}
healthcheck:
test: ["CMD", "nc", "-z", "localhost", "80"]
interval: 5s
timeout: 10s
retries: 15
start_period: 5s

monkeytype-backend:
container_name: monkeytype-backend
image: monkeytype/monkeytype-backend:latest
ports:
- "${BACKEND_PORT:-5005}:5005"
#uncomment if you want to expose the backend server (not recommended)
#ports:
# - "${BACKEND_PORT:-5005}:5005"
networks:
- public # access to the internet, e.g. google firebase
- isolated # access to the databases
restart: on-failure
environment:
- DB_NAME=monkeytype
- DB_URI=mongodb://monkeytype-mongodb:27017
- REDIS_URI=redis://monkeytype-redis:6379
- FRONTEND_URL=${MONKEYTYPE_FRONTENDURL}
- FRONTEND_URL=${BASE_URL:-http://localhost}
- RECAPTCHA_SECRET=${RECAPTCHA_SECRET:-}
- EMAIL_HOST=${EMAIL_HOST:-}
- EMAIL_PORT=${EMAIL_PORT:-}
Expand All @@ -56,18 +109,32 @@ services:
monkeytype-mongodb:
condition: service_healthy
healthcheck:
test: ["CMD", "nc", "-z", "-v", "localhost", "5005"]
test: ["CMD", "nc", "-z", "localhost", "5005"]
interval: 5s
timeout: 10s
retries: 15
start_period: 5s
labels:
- "traefik.http.middlewares.backend-prefix.stripprefix.prefixes=/api"
- "traefik.http.middlewares.backend-headers.headers.customrequestheaders.CF-Connecting-IP="
- "traefik.http.routers.backend.middlewares=backend-prefix,backend-headers"
- "traefik.http.routers.backend.rule=PathPrefix(`/api`)"
- "traefik.http.routers.backend.priority=100"
- "traefik.http.routers.backend.entrypoints=web"
- "traefik.http.services.backend.loadbalancer.server.port=5005"
- "traefik.enable=true"
- "traefik.docker.network=monkeytype-public"

monkeytype-redis:
container_name: monkeytype-redis
image: redis:6.2.6
#uncomment if you want to expose the redis server
#uncomment if you want to expose the redis server (not recommended)
#ports:
# - "${REDIS_PORT:-6379}:6379"
networks:
- isolated
#uncomment if you want to expose the redis server (not recommended)
#- public
restart: on-failure
volumes:
- redis-data:/data
Expand All @@ -84,9 +151,13 @@ services:
restart: on-failure
volumes:
- mongo-data:/data/db
#uncomment if you want to expose the mongodb server
#uncomment if you want to expose the mongodb server (not recommended)
#ports:
# - "${MONGO_PORT:-27017}:27017"
networks:
- isolated
#uncomment if you want to expose the mongodb server (not recommended)
#- public
healthcheck:
test: echo 'db.stats().ok' | mongo localhost:27017/test --quiet
interval: 5s
Expand All @@ -99,3 +170,10 @@ volumes:
name: monkeytype_mongo_data
redis-data:
name: monkeytype_redis_data

networks:
isolated:
name: monkeytype-isolated
internal: true
public:
name: monkeytype-public
16 changes: 10 additions & 6 deletions docker/example.env
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

### === Required Config ===

# URL of the frontend (accessible by browser)
MONKEYTYPE_FRONTENDURL=http://myserver:8080

# URL of the backend (accessible by browser)
MONKEYTYPE_BACKENDURL=http://myserver:5005
# By default your installation is available on http://localhost
# If you want to expose the installation on a network (private or public)
# you need to have a domain and enable the HTTPS marked entries in the docker-compose.yml
# and uncomment and set the following
#DOMAIN=
#BASE_URL=
#ACME_EMAIL=

### === Optional: Google reCAPTCHA ===

Expand Down Expand Up @@ -38,7 +40,9 @@ RECAPTCHA_SECRET=

### === Optional: Custom Ports ===

# HTTP_PORT=8080
# HTTP_PORT=80
# HTTPS_PORT=443
# FRONTEND_PORT=8080
# BACKEND_PORT=5005
# REDIS_PORT=6379
# MONGO_PORT=27017
20 changes: 16 additions & 4 deletions docs/SELF_HOSTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,34 @@
- create an `.env` file, you can copy the content from the [example.env](https://github.com/monkeytypegame/monkeytype/tree/master/docker/example.env).
- download the [backend-configuration.json](https://github.com/monkeytypegame/monkeytype/tree/master/docker/backend-configuration.json)
- run `docker compose up -d`
- after the command exits successfully you can access [http://localhost:8080](http://localhost:8080)
- after the command exits successfully you can access [http://localhost](http://localhost)

### Hosting over the network (HTTPS)

If you plan to access your self-hosted Monkeytype instance over a local network or the internet (not using `localhost`), **you must serve it over HTTPS**. Modern browsers restrict key web features, such as `crypto.randomUUID`, to secure contexts. Accessing the site via HTTP over a network will cause the frontend to crash with errors like `Uncaught TypeError: crypto.randomUUID is not a function`.

To solve this, you need to place a reverse proxy (like Nginx, Caddy, or Traefik) in front of your containers to handle HTTPS/TLS termination.
#### Enable HTTPS

Update the `.env` file and uncomment these lines and set the values based on your domain.

```
DOMAIN=mydomain.com
BASE_URL=https://mydomain.com
ACME_EMAIL=certmanager@mydomain.com
```

Update the `docker-compose.yml` and uncomment all lines marked with `# enable for HTTPS`.



#### Troubleshooting Frontend Connection Issues

If your reverse proxy is up but you see errors like `Looks like the server is experiencing unexpected down time` or network errors when fetching resources, your frontend is likely trying to communicate with the backend over unsecure HTTP, causing a **Mixed Content** block in the browser.

Ensure you configure the frontend to talk to your secure backend URL by following these rules in your `.env` file:

1. **Update the frontend and backend URL:** Set `MONKEYTYPE_FRONTENDURL` and `MONKEYTYPE_BACKENDURL` to your full HTTPS backend domain.
2. **Do not include a trailing slash:** Ensure the URL does not end with a `/` (e.g., use `https://api.yourdomain.com`, **not** `https://api.yourdomain.com/`). A trailing slash will cause `404 Not Found` errors due to double slashes in the API calls (like `//configuration`).
1. **Update the frontend and backend URL:** Set `DOMAIN` and `BASE_URL` correctly, usually `BASE_URL` is `https://DOMAIN`.
2. **Do not include a trailing slash:** Ensure the URL does not end with a `/` (e.g., use `https://yourdomain.com`, **not** `https://yourdomain.com/`). A trailing slash will cause `404 Not Found` errors due to double slashes in the API calls (like `//configuration`).
3. **Force container recreation:** Monkeytype is a Single Page Application (SPA), meaning environment variables are baked into the static JavaScript files during startup. If you change your `.env`, you must completely recreate the container for the changes to apply:

```bash
Expand Down
Loading