Services
All Vircadia's services run in a secure Docker container environment.
For maximum production security, Vircadia uses only the default distributed image by each respective vendor, built from Alpine Linux. Each container is set to read_only
to prevent any modifications to the underlying image.
Docker Compose File
$name: ${VRCA_SERVER_CONTAINER_NAME}
networks:
vircadia_internal_network:
driver: bridge
internal: true # Make this network internal to prevent direct internet access
# Create a separate network for services that need external access
vircadia_public_network:
driver: bridge
volumes:
postgres_data:
name: "${VRCA_SERVER_CONTAINER_NAME}_postgres_data"
services:
# Postgres service
vircadia_world_postgres:
image: postgres:17.5-alpine3.21
container_name: ${VRCA_SERVER_SERVICE_POSTGRES_CONTAINER_NAME}
user: "70:70" # Alpine postgres user (UID:GID)
restart: always
environment:
POSTGRES_DB: ${VRCA_SERVER_SERVICE_POSTGRES_DATABASE}
POSTGRES_USER: ${VRCA_SERVER_SERVICE_POSTGRES_SUPER_USER_USERNAME} # This user gets superuser privileges by default
POSTGRES_PASSWORD: ${VRCA_SERVER_SERVICE_POSTGRES_SUPER_USER_PASSWORD}
command: [
"postgres",
"-c", "wal_level=logical",
"-c", "max_wal_size=4GB", # Increase from default 1GB to 4GB
"-c", "checkpoint_timeout=5min", # Default is 5min, but good to explicitly set
"-c", "checkpoint_completion_target=0.9" # Spread checkpoint I/O over more time
]
ports:
- "${VRCA_SERVER_SERVICE_POSTGRES_HOST_CONTAINER_BIND_EXTERNAL}:${VRCA_SERVER_SERVICE_POSTGRES_PORT_CONTAINER_BIND_EXTERNAL}:5432"
read_only: true
tmpfs:
- /tmp
- /var/run/postgresql:uid=70,gid=70,exec
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: [
"CMD-SHELL",
"pg_isready -U ${VRCA_SERVER_SERVICE_POSTGRES_SUPER_USER_USERNAME} -d ${VRCA_SERVER_SERVICE_POSTGRES_DATABASE} && psql -U ${VRCA_SERVER_SERVICE_POSTGRES_SUPER_USER_USERNAME} -d ${VRCA_SERVER_SERVICE_POSTGRES_DATABASE} -c \"SELECT COUNT(*) FROM config.seeds\" | grep -q '[1-9]'"
]
interval: 5s
timeout: 5s
retries: 5
networks:
- vircadia_internal_network
- vircadia_public_network
# PGWEB service
vircadia_world_pgweb:
image: sosedoff/pgweb:0.16.2
container_name: ${VRCA_SERVER_SERVICE_PGWEB_CONTAINER_NAME}
user: "1000:1000" # Run as non-root user (typical first user UID/GID)
restart: always
ports:
- "${VRCA_SERVER_SERVICE_PGWEB_HOST_CONTAINER_BIND_EXTERNAL}:${VRCA_SERVER_SERVICE_PGWEB_PORT_CONTAINER_BIND_EXTERNAL}:8081"
environment:
- PGWEB_DATABASE_URL=postgres://${VRCA_SERVER_SERVICE_POSTGRES_SUPER_USER_USERNAME}:${VRCA_SERVER_SERVICE_POSTGRES_SUPER_USER_PASSWORD}@${VRCA_SERVER_SERVICE_POSTGRES_CONTAINER_NAME}:${VRCA_SERVER_SERVICE_POSTGRES_PORT_CONTAINER_BIND_EXTERNAL}/${VRCA_SERVER_SERVICE_POSTGRES_DATABASE}?sslmode=disable
depends_on:
vircadia_world_postgres:
condition: service_healthy
restart: true
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8081"]
interval: 5s
timeout: 5s
retries: 3
start_period: 5s
read_only: true
tmpfs:
- /tmp
- /var/run
- /var/cache
- /var/log
- /home/pgweb/.pgweb/bookmarks
- /home/pgweb/.pgweb/sessions
- /home/pgweb/.pgweb/queries
networks:
- vircadia_internal_network
- vircadia_public_network
# API service
vircadia_world_api_manager:
image: oven/bun:1.2.17-alpine
container_name: ${VRCA_SERVER_SERVICE_WORLD_API_MANAGER_CONTAINER_NAME}
user: "1000:1000" # Run as non-root user
restart: always
read_only: true
ports:
- "${VRCA_SERVER_SERVICE_WORLD_API_MANAGER_HOST_CONTAINER_BIND_EXTERNAL}:${VRCA_SERVER_SERVICE_WORLD_API_MANAGER_PORT_CONTAINER_BIND_EXTERNAL}:${VRCA_SERVER_SERVICE_WORLD_API_MANAGER_PORT_CONTAINER_BIND_INTERNAL}"
volumes:
- ./api/volume/app:/app
working_dir: /app
command: ["bun", "run", "dist/world.api.manager.js"]
environment:
VRCA_SERVER_DEBUG: ${VRCA_SERVER_DEBUG}
VRCA_SERVER_SUPPRESS: ${VRCA_SERVER_SUPPRESS}
VRCA_SERVER_SERVICE_POSTGRES_SUPER_USER_USERNAME: ${VRCA_SERVER_SERVICE_POSTGRES_SUPER_USER_USERNAME}
VRCA_SERVER_SERVICE_POSTGRES_AGENT_PROXY_USER_USERNAME: ${VRCA_SERVER_SERVICE_POSTGRES_AGENT_PROXY_USER_USERNAME}
VRCA_SERVER_SERVICE_POSTGRES_HOST_CONTAINER_BIND_EXTERNAL: ${VRCA_SERVER_SERVICE_POSTGRES_HOST_CONTAINER_BIND_EXTERNAL}
VRCA_SERVER_SERVICE_POSTGRES_PORT_CONTAINER_BIND_EXTERNAL: ${VRCA_SERVER_SERVICE_POSTGRES_PORT_CONTAINER_BIND_EXTERNAL}
VRCA_SERVER_SERVICE_POSTGRES_DATABASE: ${VRCA_SERVER_SERVICE_POSTGRES_DATABASE}
VRCA_SERVER_SERVICE_POSTGRES_SUPER_USER_PASSWORD: ${VRCA_SERVER_SERVICE_POSTGRES_SUPER_USER_PASSWORD}
VRCA_SERVER_SERVICE_POSTGRES_AGENT_PROXY_USER_PASSWORD: ${VRCA_SERVER_SERVICE_POSTGRES_AGENT_PROXY_USER_PASSWORD}
VRCA_SERVER_SERVICE_WORLD_API_MANAGER_HOST_CONTAINER_BIND_INTERNAL: ${VRCA_SERVER_SERVICE_WORLD_API_MANAGER_HOST_CONTAINER_BIND_INTERNAL}
VRCA_SERVER_SERVICE_WORLD_API_MANAGER_PORT_CONTAINER_BIND_INTERNAL: ${VRCA_SERVER_SERVICE_WORLD_API_MANAGER_PORT_CONTAINER_BIND_INTERNAL}
VRCA_SERVER_SERVICE_WORLD_API_MANAGER_HOST_PUBLIC_AVAILABLE_AT: ${VRCA_SERVER_SERVICE_WORLD_API_MANAGER_HOST_PUBLIC_AVAILABLE_AT}
VRCA_SERVER_SERVICE_WORLD_API_MANAGER_PORT_PUBLIC_AVAILABLE_AT: ${VRCA_SERVER_SERVICE_WORLD_API_MANAGER_PORT_PUBLIC_AVAILABLE_AT}
depends_on:
vircadia_world_postgres:
condition: service_healthy
restart: true
healthcheck:
test: ["CMD-SHELL", "wget --spider http://127.0.0.1:${VRCA_SERVER_SERVICE_WORLD_API_MANAGER_PORT_CONTAINER_BIND_INTERNAL}/stats"]
interval: 10s
timeout: 10s
retries: 3
start_period: 5s
networks:
- vircadia_internal_network
- vircadia_public_network
# State service
vircadia_world_state_manager:
image: oven/bun:1.2.17-alpine
container_name: ${VRCA_SERVER_SERVICE_WORLD_STATE_MANAGER_CONTAINER_NAME}
user: "1000:1000" # Run as non-root user
restart: always
read_only: true
ports:
- "${VRCA_SERVER_SERVICE_WORLD_STATE_MANAGER_HOST_CONTAINER_BIND_EXTERNAL}:${VRCA_SERVER_SERVICE_WORLD_STATE_MANAGER_PORT_CONTAINER_BIND_EXTERNAL}:${VRCA_SERVER_SERVICE_WORLD_STATE_MANAGER_PORT_CONTAINER_BIND_INTERNAL}"
volumes:
- ./state/volume/app:/app
working_dir: /app
command: ["bun", "run", "dist/world.state.manager.js"]
environment:
VRCA_SERVER_DEBUG: ${VRCA_SERVER_DEBUG}
VRCA_SERVER_SUPPRESS: ${VRCA_SERVER_SUPPRESS}
VRCA_SERVER_SERVICE_POSTGRES_SUPER_USER_USERNAME: ${VRCA_SERVER_SERVICE_POSTGRES_SUPER_USER_USERNAME}
VRCA_SERVER_SERVICE_POSTGRES_AGENT_PROXY_USER_USERNAME: ${VRCA_SERVER_SERVICE_POSTGRES_AGENT_PROXY_USER_USERNAME}
VRCA_SERVER_SERVICE_POSTGRES_HOST_CONTAINER_BIND_EXTERNAL: ${VRCA_SERVER_SERVICE_POSTGRES_HOST_CONTAINER_BIND_EXTERNAL}
VRCA_SERVER_SERVICE_POSTGRES_PORT_CONTAINER_BIND_EXTERNAL: ${VRCA_SERVER_SERVICE_POSTGRES_PORT_CONTAINER_BIND_EXTERNAL}
VRCA_SERVER_SERVICE_POSTGRES_DATABASE: ${VRCA_SERVER_SERVICE_POSTGRES_DATABASE}
VRCA_SERVER_SERVICE_POSTGRES_SUPER_USER_PASSWORD: ${VRCA_SERVER_SERVICE_POSTGRES_SUPER_USER_PASSWORD}
VRCA_SERVER_SERVICE_POSTGRES_AGENT_PROXY_USER_PASSWORD: ${VRCA_SERVER_SERVICE_POSTGRES_AGENT_PROXY_USER_PASSWORD}
VRCA_SERVER_SERVICE_WORLD_STATE_MANAGER_HOST_CONTAINER_BIND_INTERNAL: ${VRCA_SERVER_SERVICE_WORLD_STATE_MANAGER_HOST_CONTAINER_BIND_INTERNAL}
VRCA_SERVER_SERVICE_WORLD_STATE_MANAGER_PORT_CONTAINER_BIND_INTERNAL: ${VRCA_SERVER_SERVICE_WORLD_STATE_MANAGER_PORT_CONTAINER_BIND_INTERNAL}
depends_on:
vircadia_world_postgres:
condition: service_healthy
restart: true
healthcheck:
test: ["CMD-SHELL", "wget --spider http://127.0.0.1:${VRCA_SERVER_SERVICE_WORLD_STATE_MANAGER_PORT_CONTAINER_BIND_INTERNAL}/stats"]
interval: 10s
timeout: 10s
retries: 3
start_period: 5s
networks:
- vircadia_internal_network
- vircadia_public_network