Installation
Three supported ways to run Currencyinfo, in order of preference:
- the published container image, which is the recommended deployment
- a container built from a checkout, for contributors and for platforms without a published architecture
- a source installation, for development and for hosts where Docker is unavailable
Whichever you choose, the configuration file and the MongoDB requirements are identical.
Requirements
| Component | Version | Notes |
|---|---|---|
| Node.js | 22.12 or newer | Only for a source installation. The service runs on any Node.js 22; 22.12 is the floor both for the documentation toolchain, which resolves Vite 8 and Rolldown, and for require(esm), which the compiled build uses to load the ESM-only NestJS 12 packages. Running the test suite needs 24.9 — see contributing |
| pnpm | 12.3.4 | Pinned through packageManager; npm works but the lockfile is pnpm's |
| MongoDB | 6.0 or newer | 8.0 is what the shipped Compose file pins |
| Docker | 24 or newer | Only for container deployments |
Published container image
The image is published to the GitHub Container Registry on every stable release and is anonymously pullable:
docker pull ghcr.io/adamant-im/currencyinfo:latest| Property | Value |
|---|---|
| Registry | ghcr.io/adamant-im/currencyinfo |
| Platforms | linux/amd64, linux/arm64 |
| Base | node:22-alpine |
| Runtime user | node, UID and GID 1000 |
| Working directory | /usr/src/currencyinfo |
| Exposed port | 36661 |
| Entrypoint | node dist/main |
Tags
| Tag | Moves | Use it for |
|---|---|---|
4.2.0 | Never | Production. Pin this |
4.2 | Within a minor series | Automatic patch updates |
4 | Within a major series | Automatic minor updates |
latest | Newest stable release | Evaluation and development |
latest is published only from a reviewed release whose tag is an ancestor of master. Pre-releases never move latest, and no image is ever published from dev or from an arbitrary workflow run.
What is not in the image
The image contains the compiled application, the production dependency tree, and config.default.jsonc as a template. It contains no configuration, no credentials, no logs, and no database state. Mount your config.jsonc at runtime:
docker run -d \
--name currencyinfo \
--restart always \
-p 36661:36661 \
-v "$(pwd)/config.jsonc:/usr/src/currencyinfo/config.jsonc:ro" \
ghcr.io/adamant-im/currencyinfo:4.2.0Because the process runs as UID 1000 and Docker cannot change the ownership of a bind mount, run this once on Linux before the first start:
sudo chown 1000:1000 config.jsonc
sudo chmod 600 config.jsoncVerifying the image
Every published image carries OCI metadata, a build provenance attestation, and an SBOM:
# Labels and platforms
docker buildx imagetools inspect ghcr.io/adamant-im/currencyinfo:4.2.0
# Build provenance and SBOM attestations
docker buildx imagetools inspect ghcr.io/adamant-im/currencyinfo:4.2.0 \
--format '{{ json .Provenance }}'
docker buildx imagetools inspect ghcr.io/adamant-im/currencyinfo:4.2.0 \
--format '{{ json .SBOM }}'The labels record the source repository, the exact revision, the version, the license, and this documentation site.
Docker Compose
The repository ships docker-compose.prod.yaml, which pulls the published image and pins MongoDB to a supported major version:
curl -fsSL -o docker-compose.yaml \
https://raw.githubusercontent.com/Adamant-im/currencyinfo/master/docker-compose.prod.yaml
curl -fsSL -o config.jsonc \
https://raw.githubusercontent.com/Adamant-im/currencyinfo/master/config.default.jsonc
sudo chown 1000:1000 config.jsonc && sudo chmod 600 config.jsonc
docker compose up -dLocal overrides belong in docker-compose.override.yml, which Compose merges automatically and which the repository git-ignores:
services:
app:
ports: !override
- '127.0.0.1:36661:36661'
restart: unless-stoppedports merges by appending, not by replacing
Without the !override tag Compose keeps both mappings, so the public 36661:36661 binding from the shipped file survives and the service stays exposed on every interface. Two publishers on the same host port can also fail to start. Check the merged result before relying on it:
docker compose -f docker-compose.yaml -f docker-compose.override.yml config!override needs Compose v2.24.4 or newer; 2.24.0 through 2.24.3 do not support it. On an older Compose, edit the ports list in the main file instead of layering an override.
Building the image yourself
Contributors, and anyone on a platform outside the published manifest, can build from a checkout. docker-compose.prod.yaml carries a commented build block for exactly this:
services:
app:
# Comment out `image:` and uncomment this to build from the checkout
build:
context: .Or directly:
git clone https://github.com/Adamant-im/currencyinfo.git
cd currencyinfo
docker build -t currencyinfo:local .The Dockerfile is a two-stage build. The builder installs the full dependency tree with lifecycle scripts disabled, rebuilds only @swc/core, and compiles the project. The runtime stage installs production dependencies only and copies the compiled output.
Source installation
1. Clone and install
git clone https://github.com/Adamant-im/currencyinfo.git
cd currencyinfo
pnpm install --ignore-scripts
pnpm run deps:setup--ignore-scripts blocks lifecycle scripts across the whole dependency tree. deps:setup then rebuilds @swc/core alone, which is the one package that genuinely needs a native build step.
pnpm has to be on PATH, not only reachable through corepack
deps:setup runs pnpm rebuild @swc/core in a child shell, so invoking it as corepack pnpm run deps:setup fails: the child inherits no pnpm executable.
$ pnpm rebuild @swc/core
sh: 1: pnpm: not found
[ELIFECYCLE] Command failed with exit code 127.Install the shim once, and the commands on this page work as written:
corepack enable pnpm
pnpm --version # 12.3.4, taken from `packageManager`2. Configure
cp config.default.jsonc config.jsoncA source installation usually talks to a local MongoDB, so change the host:
{
"server": {
"mongodb": { "host": "127.0.0.1", "port": 27017, "db": "tickersdb" }
}
}config.jsonc is git-ignored. Keep it at mode 600; it holds every provider key and the ADAMANT notification passphrase.
A local MongoDB for development is available through the repository's own Compose file:
docker compose up -d3. Build and run
pnpm run build
pnpm run start:prodFor development with file watching, which also allows config.test.jsonc and falls back to config.default.jsonc:
pnpm run start:dev4. Run it as a service
A minimal systemd unit for a source installation at /opt/currencyinfo:
[Unit]
Description=Currencyinfo exchange rates service
After=network-online.target mongod.service
Wants=network-online.target
[Service]
Type=simple
User=currencyinfo
Group=currencyinfo
WorkingDirectory=/opt/currencyinfo
ExecStart=/usr/bin/node dist/main
Restart=always
RestartSec=10
# Hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/currencyinfo/logs
[Install]
WantedBy=multi-user.targetThe working directory matters: the configuration file, the logs/ directory, and the package.json the version is read from are all resolved relative to it.
sudo systemctl daemon-reload
sudo systemctl enable --now currencyinfoMigrating from Currencyinfo v1
A v1 config.json can be converted in place:
pnpm run migrate ./config.jsonThe script writes config.jsonc next to the source file and never overwrites an existing one. It also refuses to leave a source enabled that cannot work:
- CryptoCompare stays enabled only when the legacy
ccApiKeyis present, and the script reminds you to addCryptoCompareback topriorities - CoinMarketCap stays enabled only when the legacy
cmcApiKeyis present - CoinGecko is always disabled, because a v1 configuration carries no Demo key
Read every warning it prints: each one names a follow-up step.
Migrating a pre-4.1 database
Releases before 4.1.0 used a different ticker document layout. The change is not breaking for API consumers — endpoint responses and _id values are unchanged — but the stored documents must be converted:
pnpm exec node scripts/migrate-db.mjsRun it interactively so the connection string is prompted for and masked, rather than passed on a command line where it lands in shell history and in the process list. See upgrade and rollback for the full procedure.
Next steps
- Configuration reference
- Operations for reverse proxy, TLS, and health checks
- Upgrade and rollback
