docker-compose.full-stack.yml•5.85 kB
services:
postgres:
image: postgres:16
restart: always
environment:
- POSTGRES_USER=medplum
- POSTGRES_PASSWORD=medplum
command:
# We use command line args instead of a postgres.conf to avoid additional setup out of the box
- "postgres"
- "-c"
- "listen_addresses=*"
- "-c"
- "statement_timeout=60000"
- "-c"
- "default_transaction_isolation=REPEATABLE READ"
- "-c"
- "shared_preload_libraries=pg_stat_statements,auto_explain"
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U medplum"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7
restart: always
command: redis-server --requirepass medplum
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "-a", "medplum", "ping"]
interval: 10s
timeout: 5s
retries: 5
# Medplum server container
medplum-server:
image: medplum/medplum-server:latest
restart: always
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
ports:
- "8103:8103"
volumes:
# Conditionally define a volume for a `medplum.config.json` if one is specified by the MEDPLUM_CONFIG_PATH env var
- ${MEDPLUM_CONFIG_PATH:-./medplum.config.json}:/usr/src/medplum/packages/server/medplum.config.json
entrypoint: >
sh -c "
if [ -n '${MEDPLUM_CONFIG_PATH}' ]; then
echo 'Config file found, running with custom config'
node --require ./packages/server/dist/otel/instrumentation.js packages/server/dist/index.js file:$MEDPLUM_CONFIG_PATH
else
echo 'No config file found, running with default env settings'
node --require ./packages/server/dist/otel/instrumentation.js packages/server/dist/index.js env
fi
"
environment:
MEDPLUM_PORT: 8103
MEDPLUM_BASE_URL: "http://localhost:8103/"
MEDPLUM_APP_BASE_URL: "http://localhost:3000/"
MEDPLUM_STORAGE_BASE_URL: "http://localhost:8103/storage/"
MEDPLUM_DATABASE_HOST: "postgres"
MEDPLUM_DATABASE_PORT: 5432
MEDPLUM_DATABASE_DBNAME: "medplum"
MEDPLUM_DATABASE_USERNAME: "medplum"
MEDPLUM_DATABASE_PASSWORD: "medplum"
MEDPLUM_REDIS_HOST: "redis"
MEDPLUM_REDIS_PORT: 6379
MEDPLUM_REDIS_PASSWORD: "medplum"
MEDPLUM_BINARY_STORAGE: "file:./binary/"
MEDPLUM_SIGNING_KEY_ID: "my-key-id"
MEDPLUM_SIGNING_KEY: "-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,4C2E1B45FFF24610\n\n0SOZn3P0Bd9lZgv2eSWWLMQ4JqxhbJ+dWM+V1TtSwqxe3VP24z4bys5VRpmsEpqn\nROKxdXCeqAbYsLo8V9dOQvwaxo2TTWFgUFj7sQYklyr1g5S9+KCp+1B/5E7UgNDd\nhXA2u4uhz6Bck0mTPwoy3oHjNUaNBZilMdwiR3qeiGYC0DyX69+IJgwFUTt2a1jc\nU5aUyellGYa47QRZcePgyk7Cl4FcBW9YA0pS4rNpO4wNVN6GGuZti4c0Y3PHXSRE\nDse95ZN9iWBtufjpjk4s8MX0rzqMWcjbAhTs2N5YBgKsv2czm5YMdXsYH6tGL7a1\nPyNia0r1AnHAD3pK+vzaZGaLrvubZikrt7dr+Tp1U45b2YaZlMMaXwGU6WEK7kwr\n4sbl9hqQf/+oqBAdyJIgxIhFumK+ukUIlCV+b/XUuoatDXD127JwEyEM78Nzg5Bc\n/bKGEo9uehXpuIi0jp1BtegUIkfoV543PZZgslGVdzq0vXOir+PiHJBLlbWXXSAb\nEWKOQW2/bZ8JIHhi3Ag7KDlTVF1XetJ2TqYOOP9izfMp4lJ2vLtkH7P+jEKG8z6b\nurnXYkDWYEbzhG1frEssVQN0GP3wdyEK+n6LBCuj52Uje/M7LwahPX6dJRYPOpL9\nbApSNNJLahRRQREHp1wqEWism3r4+yRa4ha/BGc4dfKTsUtJEiHqdWvDzomN5C6A\nC7u3zjUv3ZZLoCLCbBUsiVdlJZJ5u/ymky5LKVbsscmZj93HE7/FL56I17bmTlDo\npvkJWk9SmVXvs3lwMMBRbykj974ZWEMw9EjCoP9rDJ0UNsy2kVRFfXoPMKL5S01D\niBRVSZB7k7qJofGtlBpDfooHOw4uAJ/6A0l8vpOm/Vpk8tdiRLL/RuzEKz5G3ltm\nrXPn83avfNc5+EvaM8IIKyPTvHegE5XszGK3NNlzUO1Ydze/xQPhdrp4QYFzJOuB\nXVIazLeXSJ5EjJ1ylWAWgNzsx+42NWeA2CZAZz+IJFw6C2iHEB8f8Nw6iJmFfm3I\nWsrvCRbuwIsW9fjtHTpOCCpxXu5EcvN5BKwFXeBatB7xqR6EnPbk6xDxZdroEKhH\nEZU4PlHu+BwTKKCwa4Ynwn1Qpu453qgNzaxgHLbdFipW5/AkreNWK5Il+5Bl8G90\no/MhO66eBXv3JbOtMUAqs9+Qyl5K1TaNqbStWmsiq+36Niz4ZRg7L/7W6zjG/hTH\npignoDyJYPjFFQ/sTsTUv0oKVI6KIYFlIHBDnGGnH09926sd+U/isSeMDP+Qa32m\nhHzScmDPsdyjdFsdXsJjZHe7mqCijGXu/LW4CoWoqln4y29c5BMJazwnIwegrLjJ\nQeW6InUhGZLy+uJbs1ZWxlqzOmMoTx2VVgoABdOHn/mQEC/AreUdvPMkVVYEuxel\nmAMOoefncx/EPxn7gY2SrEdmSnk9VuzR30KMC1qSw196QbQHR1G2vxKcXPwe/LH9\n7Pa0gwwqCaS2ggYt5Rvlxm7DeBIGHzGtPILnl1qyVGaqn64244JeLi9bY/O+E+uq\nBSgQmt2NwPK2RQgzzt/ETUXoOFHKiwS1v2Vp4H2PPDI8CzvlRralsQ==\n-----END RSA PRIVATE KEY-----"
MEDPLUM_SIGNING_KEY_PASSPHRASE: "top_secret"
MEDPLUM_SUPPORT_EMAIL: '\"Medplum\" <support@medplum.com>'
MEDPLUM_GOOGLE_CLIENT_ID: "397236612778-c0b5tnjv98frbo1tfuuha5vkme3cmq4s.apps.googleusercontent.com"
MEDPLUM_GOOGLE_CLIENT_SECRET: ""
MEDPLUM_RECAPTCHA_SITE_KEY: "6LfHdsYdAAAAAC0uLnnRrDrhcXnziiUwKd8VtLNq"
MEDPLUM_RECAPTCHA_SECRET_KEY: "6LfHdsYdAAAAAH9dN154jbJ3zpQife3xaiTvPChL"
MEDPLUM_MAX_JSON_SIZE: "1mb"
MEDPLUM_MAX_BATCH_SIZE: "50mb"
MEDPLUM_BOT_LAMBDA_ROLE_ARN: ""
MEDPLUM_BOT_LAMBDA_LAYER_NAME: "medplum-bot-layer"
MEDPLUM_VM_CONTEXT_BOTS_ENABLED: "true"
MEDPLUM_DEFAULT_BOT_RUNTIME_VERSION: "vmcontext"
MEDPLUM_ALLOWED_ORIGINS: "*"
MEDPLUM_INTROSPECTION_ENABLED: "true"
MEDPLUM_SHUTDOWN_TIMEOUT_MILLISECONDS: 30000
healthcheck:
test:
# We use Node's fetch for healthcheck because this image doesn't have a curl or wget installed
[
"CMD",
"node",
"-e",
'fetch("http://localhost:8103/healthcheck").then(r => r.json()).then(console.log).catch(() => { process.exit(1); })',
]
interval: 30s
timeout: 10s
retries: 5
# Medplum app container (web UI)
medplum-app:
image: medplum/medplum-app:latest
restart: always
depends_on:
medplum-server:
condition: service_healthy
ports:
- "3000:3000"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000"]
interval: 10s
timeout: 5s
retries: 5