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>
95 lines
2.9 KiB
Markdown
95 lines
2.9 KiB
Markdown
# 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 `HEALTHCHECK` hits `/health` every 30s. It does not call DeepL, so a
|
|
DeepL outage will not restart the container.
|