Installation
share-links is distributed as source. There is no package to install and no published container image: you clone the repository and run it with Node, or build the image locally with Docker Compose.
git clone https://github.com/Aquilabot/share-links.gitcd share-linksRequirements
Section titled “Requirements”| Path | You need |
|---|---|
| Node | Node 24 or later ("engines": { "node": ">=24" }). No npm install step: the project has no dependencies. |
| Docker Compose | Docker with the Compose plugin. The image is built from node:24-alpine. |
| Tests | Node 24 or later. CI runs the suite on Node 24 and 26. |
The service listens on port 8080 by default.
Run with Node
Section titled “Run with Node”npm startnpm start runs node --disable-warning=ExperimentalWarning src/main.mjs. With no environment set, the service:
- listens on all interfaces, port
8080; - uses
http://localhost:8080asPUBLIC_ORIGIN, the only accepted browser origin and the base of every returned URL; - creates
./data(relative to the working directory) withlinks.sqlite,images.sqliteand abackups/folder; - writes one JSON line per event and per request to stdout.
The first line says it is listening:
{"time":"2026-10-24T09:00:00.000Z","event":"listening","port":8080}Configuration comes from environment variables. For example, to serve an app at https://app.example.com from a proxy on the same host:
PUBLIC_ORIGIN=https://app.example.com \TRUSTED_PROXIES=127.0.0.1/32 \HOST=127.0.0.1 \DATA_DIR=/var/lib/share-links \npm startA malformed value stops the service at start with an error. Every variable is listed in the configuration reference.
SIGTERM and SIGINT close the server and both databases, then exit. If open connections keep the server from closing, the process exits after 5 seconds anyway.
Run with Docker Compose
Section titled “Run with Docker Compose”docker compose up --buildThen check http://localhost:8080/api/health. The repository’s compose.yaml builds the image and runs it with these settings:
services: share-links: build: . image: share-links:1 restart: unless-stopped environment: PUBLIC_ORIGIN: http://localhost:8080 LINK_TARGET_PATH: / LINK_MISSING_PATH: /link-not-found TRUSTED_PROXIES: "" ports: - "127.0.0.1:8080:8080" volumes: - data:/data read_only: true tmpfs: - /tmp:size=8m cap_drop: - ALL security_opt: - no-new-privileges:true mem_limit: 128m cpus: 0.5 pids_limit: 64 logging: driver: json-file options: max-size: 10m max-file: "3"
volumes: data:What the image and the compose file do:
- The
Dockerfilecopies onlypackage.jsonandsrc/. Tests, the benchmark, the README andopenapi.yamlare excluded by.dockerignore. - The process runs as the image’s unprivileged
nodeuser (uid 1000), which owns only/data.DATA_DIR=/dataandPORT=8080are set in the image. - The root filesystem is read-only. The service writes only to the
datavolume mounted at/data. - The port is published on
127.0.0.1only. A reverse proxy on the host is expected in front of it. - The image has a
HEALTHCHECKthat fetcheshttp://127.0.0.1:8080/api/healthevery 30 seconds and fails on any non-2xx answer.
For a real deployment, set PUBLIC_ORIGIN to the app’s origin and TRUSTED_PROXIES to the proxy’s address range. See running behind a reverse proxy.
Run the tests
Section titled “Run the tests”npm testnpm test runs node --disable-warning=ExperimentalWarning --test. CI runs node --test on Node 24 and 26 on every push to main and on pull requests.
The suite has 41 tests in four files under tests/: http.test.mjs, links.test.mjs, images.test.mjs and units.test.mjs. Each HTTP test starts its own server on a random port on 127.0.0.1 with temporary databases, so the suite needs no running service and no network. Every address in the tests is 127.0.0.1 or from the documentation ranges.
The tests are not part of the container image. Run them from a checkout.
Written from the code and tests of each repository. If a page and the code disagree, the code is right and the page has a bug. Open an issue.
© 2026 Jean Carlo Benites Porras. Code samples are MIT, like the projects they come from.