domains.json was written next to server.js, i.e. into the image filesystem, so every redeploy discarded the domain -> glossary_id mappings. Its path is now overridable via DOMAINS_FILE and the image defaults it to /app/data, a subdirectory so that mounting a volume there does not shadow server.js and index.html in /app. Also adds a GET /health endpoint plus a Docker HEALTHCHECK that uses it. The probe deliberately does not call DeepL, so an upstream DeepL outage does not get the container restarted. .gitignore is new; .env holds a real API key and was previously untracked only by convention. Co-Authored-By: Claude <noreply@anthropic.com>
2.9 KiB
Deploying to Dokploy
The app is a single Node process with no dependencies. Dokploy builds it from
the Dockerfile in this repo and puts Traefik in front of it.
1. Push the repo to a Git host
Dokploy pulls from GitHub/GitLab/Gitea (or a raw Git URL over SSH).
Before the first push: .env and .env.testcopy contain a real DeepL API
key. They are untracked from now on via .gitignore, but if they were already
committed they still exist in history — remove them from tracking and rotate
the key rather than relying on the file being gone from the working tree.
2. Create the application in Dokploy
Project → Create Service → Application.
| Field | Value |
|---|---|
| Source | Git provider + this repo/branch |
| Build Type | Dockerfile |
| Dockerfile | Dockerfile |
3. Environment variables
Environment tab:
DEEPL_API_KEY=<your DeepL Growth API key>
PORT and DOMAINS_FILE are already set in the Dockerfile — do not override
them unless you also change the volume mount path to match.
The key is server-side only; it is never sent to the browser.
4. Volume (required)
Without this, every redeploy resets all glossary mappings.
Advanced → Volumes → Add :
| Field | Value |
|---|---|
| Type | Volume Mount |
| Volume Name | deepl-data |
| Mount Path | /app/data |
DOMAINS_FILE=/app/data/domains.json writes into that volume. The path is a
subdirectory of /app on purpose — mounting /app itself would hide
server.js and index.html.
5. Domain
Domains tab → Add:
| Field | Value |
|---|---|
| Host | your hostname |
| Container Port | 3000 |
| HTTPS | on, Let's Encrypt |
6. Deploy
Hit Deploy. Verify with:
curl https://<your-host>/health
# {"status":"ok","apiKeyConfigured":true}
apiKeyConfigured: false means DEEPL_API_KEY did not reach the container.
Seeding existing glossary mappings
A fresh volume starts empty and the app writes a default domains.json with
all four domains and no glossaries. The domains.json in this repo is not
copied into the image (it is in .dockerignore), so any glossary IDs recorded
there are not carried over. Either re-save the glossaries through the UI after
the first deploy, or copy the file in once:
docker cp domains.json <container>:/app/data/domains.json
docker restart <container>
Notes
- The app has no authentication. Anything on the public domain can use your DeepL quota and edit the shared glossaries. Put Traefik basic auth or an IP allowlist in front of it if it is internet-facing.
- Docker
HEALTHCHECKhits/healthevery 30s. It does not call DeepL, so a DeepL outage will not restart the container.