feat(docker,cache,proxy): improve container runtime defaults and add configurable connection limits

This commit is contained in:
2026-03-26 16:21:45 +00:00
parent 1ea38ed5d2
commit fa96a41e68
11 changed files with 218 additions and 104 deletions

View File

@@ -18,9 +18,17 @@ WORKDIR /app
COPY --from=build /app /app
ENV DCROUTER_MODE=OCI_CONTAINER
ENV DCROUTER_HEAP_SIZE=512
ENV UV_THREADPOOL_SIZE=16
RUN pnpm install -g @servezone/healthy
HEALTHCHECK --interval=30s --timeout=30s --start-period=30s --retries=3 CMD [ "healthy" ]
HEALTHCHECK --interval=30s --timeout=10s --start-period=120s --retries=3 CMD [ "healthy" ]
EXPOSE 80
CMD ["npm", "start"]
LABEL org.opencontainers.image.title="dcrouter" \
org.opencontainers.image.description="Multi-service datacenter gateway" \
org.opencontainers.image.source="https://code.foss.global/serve.zone/dcrouter"
# HTTP/HTTPS, SMTP/Submission/SMTPS, DNS, RADIUS, OpsServer, RemoteIngress, dynamic range
EXPOSE 80 443 25 587 465 53/tcp 53/udp 1812/udp 1813/udp 3000 8443 29000-30000
CMD ["sh", "-c", "node --max_old_space_size=${DCROUTER_HEAP_SIZE} ./cli.js"]

View File

@@ -1,5 +1,13 @@
# Changelog
## 2026-03-26 - 11.11.0 - feat(docker,cache,proxy)
improve container runtime defaults and add configurable connection limits
- replace the embedded cache backend integration from smartmongo LocalTsmDb to smartdb LocalSmartDb
- add OCI container settings for heap size, threadpool size, expanded exposed ports, image metadata, and a direct node startup command
- introduce startup checks for file descriptor limits and warn when container nofile limits are too low for production
- set gateway-oriented SmartProxy default limits and allow max connections, per-IP connections, and rate limits to be configured through OCI environment variables
## 2026-03-26 - 11.10.7 - fix(sms)
update sms service to use async ProjectInfo initialization

View File

@@ -13,7 +13,7 @@
"license": "MIT",
"scripts": {
"test": "(tstest test/ --logfile --timeout 60)",
"start": "(node --max_old_space_size=250 ./cli.js)",
"start": "(node ./cli.js)",
"startTs": "(node cli.ts.js)",
"build": "(tsbuild tsfolders --allowimplicitany && npm run bundle)",
"build:docker": "tsdocker build --verbose",
@@ -42,18 +42,18 @@
"@push.rocks/qenv": "^6.1.3",
"@push.rocks/smartacme": "^9.3.0",
"@push.rocks/smartdata": "^7.1.3",
"@push.rocks/smartdb": "^1.0.1",
"@push.rocks/smartdns": "^7.9.0",
"@push.rocks/smartfile": "^13.1.2",
"@push.rocks/smartfs": "^1.5.0",
"@push.rocks/smartguard": "^3.1.0",
"@push.rocks/smartjwt": "^2.2.1",
"@push.rocks/smartlog": "^3.2.1",
"@push.rocks/smartmetrics": "^3.0.3",
"@push.rocks/smartmongo": "^5.1.1",
"@push.rocks/smartmta": "^5.3.1",
"@push.rocks/smartnetwork": "^4.4.0",
"@push.rocks/smartnetwork": "^4.5.2",
"@push.rocks/smartpath": "^6.0.0",
"@push.rocks/smartpromise": "^4.2.3",
"@push.rocks/smartproxy": "^26.2.4",
"@push.rocks/smartproxy": "^26.3.0",
"@push.rocks/smartradius": "^1.1.1",
"@push.rocks/smartrequest": "^5.0.1",
"@push.rocks/smartrx": "^3.0.10",

122
pnpm-lock.yaml generated
View File

@@ -44,12 +44,15 @@ importers:
'@push.rocks/smartdata':
specifier: ^7.1.3
version: 7.1.3(socks@2.8.7)
'@push.rocks/smartdb':
specifier: ^1.0.1
version: 1.0.1
'@push.rocks/smartdns':
specifier: ^7.9.0
version: 7.9.0
'@push.rocks/smartfile':
specifier: ^13.1.2
version: 13.1.2
'@push.rocks/smartfs':
specifier: ^1.5.0
version: 1.5.0
'@push.rocks/smartguard':
specifier: ^3.1.0
version: 3.1.0
@@ -62,15 +65,12 @@ importers:
'@push.rocks/smartmetrics':
specifier: ^3.0.3
version: 3.0.3
'@push.rocks/smartmongo':
specifier: ^5.1.1
version: 5.1.1(socks@2.8.7)
'@push.rocks/smartmta':
specifier: ^5.3.1
version: 5.3.1
'@push.rocks/smartnetwork':
specifier: ^4.4.0
version: 4.4.0
specifier: ^4.5.2
version: 4.5.2
'@push.rocks/smartpath':
specifier: ^6.0.0
version: 6.0.0
@@ -78,8 +78,8 @@ importers:
specifier: ^4.2.3
version: 4.2.3
'@push.rocks/smartproxy':
specifier: ^26.2.4
version: 26.2.4
specifier: ^26.3.0
version: 26.3.0
'@push.rocks/smartradius':
specifier: ^1.1.1
version: 1.1.1
@@ -1125,6 +1125,9 @@ packages:
'@push.rocks/smartdata@7.1.3':
resolution: {integrity: sha512-7vQJ9pdRk450yn2m9tmGPdSRlQVmxFPZjHD4sGYsfqCQPg+GLFusu+H16zpf+jKzAq4F2ZBMPaYymJHXvXiVcw==}
'@push.rocks/smartdb@1.0.1':
resolution: {integrity: sha512-m79Gzn7rHBMRYDohy1Mwm3niyRrre0g1ev3NxrIO6dnjo584pa3b0vTKwN+8R+tURJPppiMWfkPscpeKtrwIIg==}
'@push.rocks/smartdelay@3.0.5':
resolution: {integrity: sha512-mUuI7kj2f7ztjpic96FvRIlf2RsKBa5arw81AHNsndbxO6asRcxuWL8dTVxouEIK8YsBUlj0AsrCkHhMbLQdHw==}
@@ -1230,8 +1233,11 @@ packages:
'@push.rocks/smartmustache@3.0.2':
resolution: {integrity: sha512-G3LyRXoJhyM+iQhkvP/MR/2WYMvC9U7zc2J44JxUM5tPdkQ+o3++FbfRtnZj6rz5X/A7q03//vsxPitVQwoi2Q==}
'@push.rocks/smartnetwork@4.4.0':
resolution: {integrity: sha512-OvFtz41cvQ7lcXwaIOhghNUUlNoMxvwKDctbDvMyuZyEH08SpLjhyv2FuKbKL/mgwA/WxakTbohoC8SW7t+kiw==}
'@push.rocks/smartnetwork@4.5.2':
resolution: {integrity: sha512-lbMMyc2f/WWd5+qzZyF1ynXndjCtasxPWmj/d8GUuis9rDrW7sLIT1PlAPC2F6Qsy4H/K32JrYU+01d/6sWObg==}
'@push.rocks/smartnftables@1.0.1':
resolution: {integrity: sha512-o822GH4J8dlEBvNLbm+CwU4h6isMUEh03tf2ZnOSWXc5iewRDdKdOCDwI/e+WdnGYWyv7gvH0DHztCmne6rTCg==}
'@push.rocks/smartnpm@2.0.6':
resolution: {integrity: sha512-7anKDOjX6gXWs1IAc+YWz9ZZ8gDsTwaLh+CxRnGHjAawOmK788NrrgVCg2Fb3qojrPnoxecc46F8Ivp1BT7Izw==}
@@ -1251,14 +1257,11 @@ packages:
'@push.rocks/smartpdf@4.2.0':
resolution: {integrity: sha512-+egzby5QKJGO10MDvWp+N69cJ8i5M354l9ntc+uLRpxuq/FEY9kigpRwMvRYF5qwOBTuGTLqvmvILzlLtboAQg==}
'@push.rocks/smartping@1.0.8':
resolution: {integrity: sha512-Fvx1Db6hSsDOI6pdiCuS9GjtOX8ugx865YQrPg5vK2iw6Qj/srwyXcWLFYt+19WVKtvtWDJIAKbW+q3bXFsCeA==}
'@push.rocks/smartpromise@4.2.3':
resolution: {integrity: sha512-Ycg/TJR+tMt+S3wSFurOpEoW6nXv12QBtKXgBcjMZ4RsdO28geN46U09osPn9N9WuwQy1PkmTV5J/V4F9U8qEw==}
'@push.rocks/smartproxy@26.2.4':
resolution: {integrity: sha512-TPFqcPIuNiU5suGCkF6A8gK10DEFfr43FOMzIVjZcF1mSbjvHFKKiT+zsy90YYPpio9bLIMmkWu7C6OtL1D4nQ==}
'@push.rocks/smartproxy@26.3.0':
resolution: {integrity: sha512-pdq2l/2vfaZGpNKDo/fG6+9qTcGv3pWzkYcBo3rSHhQvRLuYpYeFDxTKp7+cQpn0j+H2/X5SlECQtTQNnkF4qw==}
'@push.rocks/smartpuppeteer@2.0.5':
resolution: {integrity: sha512-yK/qSeWVHIGWRp3c8S5tfdGP6WCKllZC4DR8d8CQlEjszOSBmHtlTdyyqOMBZ/BA4kd+eU5f3A1r4K2tGYty1g==}
@@ -2034,9 +2037,6 @@ packages:
'@types/node@25.5.0':
resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==}
'@types/ping@0.4.4':
resolution: {integrity: sha512-ifvo6w2f5eJYlXm+HiVx67iJe8WZp87sfa683nlqED5Vnt9Z93onkokNoWqOG21EaE8fMxyKPobE+mkPEyxsdw==}
'@types/randomatic@3.1.5':
resolution: {integrity: sha512-VCwCTw6qh1pRRw+5rNTAwqPmf6A+hdrkdM7dBpZVmhl7g+em3ONXlYK/bWPVKqVGMWgP0d1bog8Vc/X6zRwRRQ==}
@@ -2919,9 +2919,6 @@ packages:
resolution: {integrity: sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==}
engines: {node: '>=20'}
isopen@1.3.0:
resolution: {integrity: sha512-AN6Q9J0UlqHFl1fN/2xJCHCBLCBCFDjZhpGBO1gh3wzgRPsFSFBUL36I2Lbfd9qkuoj58axmE7j83iejTQsk8Q==}
jackspeak@4.2.3:
resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==}
engines: {node: 20 || >=22}
@@ -3088,6 +3085,10 @@ packages:
math-random@1.0.4:
resolution: {integrity: sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==}
maxmind@5.0.5:
resolution: {integrity: sha512-1lcH2kMjbBpCFhuHaMU32vz8CuOsKttRcWMQyXvtlklopCzN7NNHSVR/h9RYa8JPuFTGmkn2vYARm+7cIGuqDw==}
engines: {node: '>=12', npm: '>=6'}
mdast-util-find-and-replace@3.0.2:
resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==}
@@ -3270,6 +3271,10 @@ packages:
mitt@3.0.1:
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
mmdb-lib@3.0.2:
resolution: {integrity: sha512-7e87vk0DdWT647wjcfEtWeMtjm+zVGqNohN/aeIymbUfjHQ2T4Sx5kM+1irVDBSloNC3CkGKxswdMoo8yhqTDg==}
engines: {node: '>=10', npm: '>=6'}
monaco-editor@0.55.1:
resolution: {integrity: sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==}
@@ -3559,10 +3564,6 @@ packages:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
ping@0.4.4:
resolution: {integrity: sha512-56ZMC0j7SCsMMLdOoUg12VZCfj/+ZO+yfOSjaNCRrmZZr6GLbN2X/Ui56T15dI8NhiHckaw5X2pvyfAomanwqQ==}
engines: {node: '>=4.0.0'}
pixelmatch@5.3.0:
resolution: {integrity: sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==}
hasBin: true
@@ -3938,12 +3939,6 @@ packages:
symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
systeminformation@5.31.4:
resolution: {integrity: sha512-lZppDyQx91VdS5zJvAyGkmwe+Mq6xY978BDUG2wRkWE+jkmUF5ti8cvOovFQoN5bvSFKCXVkyKEaU5ec3SJiRg==}
engines: {node: '>=8.0.0'}
os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
hasBin: true
tagged-tag@1.0.0:
resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==}
engines: {node: '>=20'}
@@ -3966,6 +3961,10 @@ packages:
through2@4.0.2:
resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==}
tiny-lru@11.4.7:
resolution: {integrity: sha512-w/Te7uMUVeH0CR8vZIjr+XiN41V+30lkDdK+NRIDCUYKKuL9VcmaUEmaPISuwGhLlrTGh5yu18lENtR9axSxYw==}
engines: {node: '>=12'}
tiny-worker@2.3.0:
resolution: {integrity: sha512-pJ70wq5EAqTAEl9IkGzA+fN0836rycEuz2Cn6yeZ6FRzlVS5IDOkFHpIoEsksPRQV34GDqXm65+OlnZqUSyK2g==}
@@ -5094,7 +5093,7 @@ snapshots:
'@push.rocks/smartjson': 6.0.0
'@push.rocks/smartlog': 3.2.1
'@push.rocks/smartmongo': 5.1.1(socks@2.8.7)
'@push.rocks/smartnetwork': 4.4.0
'@push.rocks/smartnetwork': 4.5.2
'@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrequest': 5.0.1
@@ -5856,7 +5855,7 @@ snapshots:
'@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartdns': 7.9.0
'@push.rocks/smartlog': 3.2.1
'@push.rocks/smartnetwork': 4.4.0
'@push.rocks/smartnetwork': 4.5.2
'@push.rocks/smartstring': 4.1.0
'@push.rocks/smarttime': 4.2.3
'@push.rocks/smartunique': 3.0.9
@@ -5866,11 +5865,14 @@ snapshots:
- '@aws-sdk/credential-providers'
- '@mongodb-js/zstd'
- '@nuxt/kit'
- bare-abort-controller
- bare-buffer
- encoding
- gcp-metadata
- kerberos
- mongodb-client-encryption
- react
- react-native-b4a
- snappy
- socks
- supports-color
@@ -6013,6 +6015,15 @@ snapshots:
- supports-color
- vue
'@push.rocks/smartdb@1.0.1':
dependencies:
'@push.rocks/smartfs': 1.5.0
'@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartrx': 3.0.10
bson: 7.2.0
mingo: 7.2.0
'@push.rocks/smartdelay@3.0.5':
dependencies:
'@push.rocks/smartpromise': 4.2.3
@@ -6284,17 +6295,19 @@ snapshots:
dependencies:
handlebars: 4.7.8
'@push.rocks/smartnetwork@4.4.0':
'@push.rocks/smartnetwork@4.5.2':
dependencies:
'@push.rocks/smartdns': 7.9.0
'@push.rocks/smartping': 1.0.8
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartstring': 4.1.0
isopen: 1.3.0
systeminformation: 5.31.4
'@push.rocks/smartrust': 1.3.2
maxmind: 5.0.5
transitivePeerDependencies:
- supports-color
'@push.rocks/smartnftables@1.0.1':
dependencies:
'@push.rocks/smartlog': 3.2.1
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartnpm@2.0.6':
dependencies:
'@push.rocks/consolecolor': 2.0.3
@@ -6346,7 +6359,7 @@ snapshots:
'@push.rocks/smartdelay': 3.0.5
'@push.rocks/smartfs': 1.5.0
'@push.rocks/smartjimp': 1.2.0
'@push.rocks/smartnetwork': 4.4.0
'@push.rocks/smartnetwork': 4.5.2
'@push.rocks/smartpath': 6.0.0
'@push.rocks/smartpromise': 4.2.3
'@push.rocks/smartpuppeteer': 2.0.5(typescript@6.0.2)
@@ -6368,17 +6381,13 @@ snapshots:
- utf-8-validate
- vue
'@push.rocks/smartping@1.0.8':
dependencies:
'@types/ping': 0.4.4
ping: 0.4.4
'@push.rocks/smartpromise@4.2.3': {}
'@push.rocks/smartproxy@26.2.4':
'@push.rocks/smartproxy@26.3.0':
dependencies:
'@push.rocks/smartcrypto': 2.0.4
'@push.rocks/smartlog': 3.2.1
'@push.rocks/smartnftables': 1.0.1
'@push.rocks/smartrust': 1.3.2
'@tsclass/tsclass': 9.5.0
minimatch: 10.2.4
@@ -7413,8 +7422,6 @@ snapshots:
dependencies:
undici-types: 7.18.2
'@types/ping@0.4.4': {}
'@types/randomatic@3.1.5': {}
'@types/relateurl@0.2.33': {}
@@ -8346,8 +8353,6 @@ snapshots:
isexe@4.0.0: {}
isopen@1.3.0: {}
jackspeak@4.2.3:
dependencies:
'@isaacs/cliui': 9.0.0
@@ -8550,6 +8555,11 @@ snapshots:
math-random@1.0.4: {}
maxmind@5.0.5:
dependencies:
mmdb-lib: 3.0.2
tiny-lru: 11.4.7
mdast-util-find-and-replace@3.0.2:
dependencies:
'@types/mdast': 4.0.4
@@ -8911,6 +8921,8 @@ snapshots:
mitt@3.0.1: {}
mmdb-lib@3.0.2: {}
monaco-editor@0.55.1:
dependencies:
dompurify: 3.2.7
@@ -9169,8 +9181,6 @@ snapshots:
picomatch@4.0.3: {}
ping@0.4.4: {}
pixelmatch@5.3.0:
dependencies:
pngjs: 6.0.0
@@ -9687,8 +9697,6 @@ snapshots:
symbol-tree@3.2.4: {}
systeminformation@5.31.4: {}
tagged-tag@1.0.0: {}
tar-fs@3.1.2:
@@ -9742,6 +9750,8 @@ snapshots:
dependencies:
readable-stream: 3.6.2
tiny-lru@11.4.7: {}
tiny-worker@2.3.0:
dependencies:
esm: 3.2.25

View File

@@ -83,7 +83,7 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
### 💾 Persistent Storage & Caching
- **Multiple storage backends**: filesystem, custom functions, or in-memory
- **Embedded cache database** via smartdata + LocalTsmDb (MongoDB-compatible)
- **Embedded cache database** via smartdata + smartdb (MongoDB-compatible)
- **Automatic TTL-based cleanup** for cached emails and IP reputation data
### 🖥️ OpsServer Dashboard
@@ -340,7 +340,7 @@ graph TB
| **OpsServer** | `@api.global/typedserver` | Web dashboard + TypedRequest API for monitoring and management |
| **MetricsManager** | `@push.rocks/smartmetrics` | Real-time metrics collection (CPU, memory, email, DNS, security) |
| **StorageManager** | built-in | Pluggable key-value storage (filesystem, custom, or in-memory) |
| **CacheDb** | `@push.rocks/smartdata` | Embedded MongoDB-compatible database (LocalTsmDb) for persistent caching |
| **CacheDb** | `@push.rocks/smartdb` | Embedded MongoDB-compatible database (LocalSmartDb) for persistent caching |
### How It Works
@@ -1066,7 +1066,7 @@ Used for: TLS certificates, DKIM keys, email routes, bounce/suppression lists, I
### Cache Database
An embedded MongoDB-compatible database (via smartdata + LocalTsmDb) for persistent caching with automatic TTL cleanup:
An embedded MongoDB-compatible database (via smartdata + smartdb) for persistent caching with automatic TTL cleanup:
```typescript
cacheConfig: {
@@ -1406,37 +1406,58 @@ tstest test/test.opsserver-api.ts --verbose --timeout 60
## Docker / OCI Container Deployment
DcRouter ships with a `Dockerfile` and supports environment-variable-driven configuration for OCI container deployments. When `DCROUTER_MODE=OCI_CONTAINER` is set, DcRouter automatically reads configuration from environment variables (and optionally from a JSON config file).
DcRouter ships with a production-ready `Dockerfile` and supports environment-variable-driven configuration for OCI container deployments. The container image includes tini as PID 1 (via the base image), proper health checks, and configurable resource limits. When `DCROUTER_MODE=OCI_CONTAINER` is set, DcRouter automatically reads configuration from environment variables (and optionally from a JSON config file).
### Running with Docker
```bash
docker run -d \
-e DCROUTER_MODE=OCI_CONTAINER \
--ulimit nofile=65536:65536 \
-e DCROUTER_TLS_EMAIL=admin@example.com \
-e DCROUTER_PUBLIC_IP=203.0.113.1 \
-e DCROUTER_DNS_NS_DOMAINS=ns1.example.com,ns2.example.com \
-e DCROUTER_DNS_SCOPES=example.com \
-p 80:80 -p 443:443 -p 25:25 -p 53:53/udp -p 3000:3000 \
-p 80:80 -p 443:443 -p 25:25 -p 587:587 -p 465:465 \
-p 53:53/udp -p 3000:3000 -p 8443:8443 \
code.foss.global/serve.zone/dcrouter:latest
```
> ⚡ **Production tip:** Always set `--ulimit nofile=65536:65536` for production deployments. DcRouter will log a warning at startup if the file descriptor limit is below 65536.
### Environment Variables
| Variable | Description | Example |
|----------|-------------|---------|
| `DCROUTER_MODE` | Set to `OCI_CONTAINER` to enable container mode | `OCI_CONTAINER` |
| `DCROUTER_CONFIG_PATH` | Path to a JSON config file (loaded as base, env vars override) | `/config/dcrouter.json` |
| `DCROUTER_BASE_DIR` | Override base data directory | `/data/dcrouter` |
| `DCROUTER_TLS_EMAIL` | ACME contact email | `admin@example.com` |
| `DCROUTER_TLS_DOMAIN` | Primary TLS domain | `example.com` |
| `DCROUTER_PUBLIC_IP` | Public IP for DNS records | `203.0.113.1` |
| `DCROUTER_PROXY_IPS` | Comma-separated ingress proxy IPs | `198.51.100.1,198.51.100.2` |
| `DCROUTER_DNS_NS_DOMAINS` | Comma-separated nameserver domains | `ns1.example.com,ns2.example.com` |
| `DCROUTER_DNS_SCOPES` | Comma-separated authoritative domains | `example.com,other.com` |
| `DCROUTER_EMAIL_HOSTNAME` | SMTP server hostname | `mail.example.com` |
| `DCROUTER_EMAIL_PORTS` | Comma-separated email ports | `25,587,465` |
| `DCROUTER_CACHE_ENABLED` | Enable/disable cache database | `true` |
| Variable | Description | Default | Example |
|----------|-------------|---------|---------|
| `DCROUTER_MODE` | Container mode (set automatically in image) | `OCI_CONTAINER` | — |
| `DCROUTER_CONFIG_PATH` | Path to JSON config file (env vars override) | — | `/config/dcrouter.json` |
| `DCROUTER_BASE_DIR` | Base data directory | `~/.serve.zone/dcrouter` | `/data/dcrouter` |
| `DCROUTER_TLS_EMAIL` | ACME contact email | — | `admin@example.com` |
| `DCROUTER_TLS_DOMAIN` | Primary TLS domain | — | `example.com` |
| `DCROUTER_PUBLIC_IP` | Public IP for DNS records | — | `203.0.113.1` |
| `DCROUTER_PROXY_IPS` | Comma-separated ingress proxy IPs | — | `198.51.100.1,198.51.100.2` |
| `DCROUTER_DNS_NS_DOMAINS` | Comma-separated nameserver domains | — | `ns1.example.com,ns2.example.com` |
| `DCROUTER_DNS_SCOPES` | Comma-separated authoritative domains | — | `example.com,other.com` |
| `DCROUTER_EMAIL_HOSTNAME` | SMTP server hostname | — | `mail.example.com` |
| `DCROUTER_EMAIL_PORTS` | Comma-separated email ports | — | `25,587,465` |
| `DCROUTER_CACHE_ENABLED` | Enable/disable cache database | `true` | `false` |
| `DCROUTER_HEAP_SIZE` | Node.js V8 heap size in MB | `512` | `1024` |
| `DCROUTER_MAX_CONNECTIONS` | Global max concurrent connections | `50000` | `100000` |
| `DCROUTER_MAX_CONNECTIONS_PER_IP` | Max connections per source IP | `100` | `200` |
| `DCROUTER_CONNECTION_RATE_LIMIT` | Max new connections/min per IP | `600` | `1200` |
### Exposed Ports
The container exposes all service ports:
| Port(s) | Protocol | Service |
|---------|----------|---------|
| 80, 443 | TCP | HTTP/HTTPS (SmartProxy) |
| 25, 587, 465 | TCP | SMTP, Submission, SMTPS |
| 53 | TCP/UDP | DNS |
| 1812, 1813 | UDP | RADIUS auth/acct |
| 3000 | TCP | OpsServer dashboard |
| 8443 | TCP | Remote ingress tunnels |
| 2900030000 | TCP | Dynamic port range |
### Building the Image

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@serve.zone/dcrouter',
version: '11.10.7',
version: '11.11.0',
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
}

View File

@@ -15,15 +15,15 @@ export interface ICacheDbOptions {
}
/**
* CacheDb - Wrapper around LocalTsmDb and smartdata
* CacheDb - Wrapper around LocalSmartDb and smartdata
*
* Provides persistent caching using smartdata as the ORM layer
* and LocalTsmDb as the embedded database engine.
* and LocalSmartDb as the embedded database engine.
*/
export class CacheDb {
private static instance: CacheDb | null = null;
private localTsmDb!: plugins.smartmongo.LocalTsmDb;
private localSmartDb!: plugins.smartdb.LocalSmartDb;
private smartdataDb!: plugins.smartdata.SmartdataDb;
private options: Required<ICacheDbOptions>;
private isStarted: boolean = false;
@@ -55,8 +55,8 @@ export class CacheDb {
/**
* Start the cache database
* - Initializes LocalTsmDb with file persistence
* - Connects smartdata to the LocalTsmDb via Unix socket
* - Initializes LocalSmartDb with file persistence
* - Connects smartdata to the LocalSmartDb via Unix socket
*/
public async start(): Promise<void> {
if (this.isStarted) {
@@ -68,16 +68,16 @@ export class CacheDb {
// Ensure storage directory exists
await plugins.fsUtils.ensureDir(this.options.storagePath);
// Create LocalTsmDb instance
this.localTsmDb = new plugins.smartmongo.LocalTsmDb({
// Create LocalSmartDb instance
this.localSmartDb = new plugins.smartdb.LocalSmartDb({
folderPath: this.options.storagePath,
});
// Start LocalTsmDb and get connection info
const connectionInfo = await this.localTsmDb.start();
// Start LocalSmartDb and get connection info
const connectionInfo = await this.localSmartDb.start();
if (this.options.debug) {
logger.log('debug', `LocalTsmDb started with URI: ${connectionInfo.connectionUri}`);
logger.log('debug', `LocalSmartDb started with URI: ${connectionInfo.connectionUri}`);
}
// Initialize smartdata with the connection URI
@@ -109,9 +109,9 @@ export class CacheDb {
await this.smartdataDb.close();
}
// Stop LocalTsmDb
if (this.localTsmDb) {
await this.localTsmDb.stop();
// Stop LocalSmartDb
if (this.localSmartDb) {
await this.localSmartDb.stop();
}
this.isStarted = false;

View File

@@ -528,10 +528,36 @@ export class DcRouter {
}
public async start() {
await this.checkSystemLimits();
logger.log('info', 'Starting DcRouter Services');
await this.serviceManager.start();
this.logStartupSummary();
}
/**
* Detect OS-level resource limits and warn if they are too low for production use.
* This is detection only — no attempts to raise limits.
*/
private async checkSystemLimits(): Promise<void> {
try {
const fs = new plugins.smartfs.SmartFs(new plugins.smartfs.SmartFsProviderNode());
const limitsContent = await fs.file('/proc/self/limits').encoding('utf8').read() as string;
const nofileLine = limitsContent.split('\n').find((line: string) => line.startsWith('Max open files'));
if (nofileLine) {
const parts = nofileLine.split(/\s{2,}/);
const softLimit = parseInt(parts[1], 10);
const hardLimit = parseInt(parts[2], 10);
if (softLimit < 65536) {
logger.log('warn', `File descriptor soft limit is ${softLimit} (hard: ${hardLimit}). ` +
`For production use, set --ulimit nofile=65536:65536 on the container runtime.`);
} else {
logger.log('info', `File descriptor limits: soft=${softLimit}, hard=${hardLimit}`);
}
}
} catch {
// Non-Linux or /proc not available — silently skip
}
}
/**
* Log comprehensive startup summary
@@ -708,9 +734,28 @@ export class DcRouter {
// Track cert entries loaded from cert store so we can populate certificateStatusMap after start
const loadedCertEntries: Array<{domain: string; publicKey: string; validUntil?: number; validFrom?: number}> = [];
// Create SmartProxy configuration
// Create SmartProxy configuration with sensible gateway defaults.
// User's smartProxyConfig overrides these defaults via spread.
const smartProxyConfig: plugins.smartproxy.ISmartProxyOptions = {
// --- dcrouter gateway defaults ---
maxConnectionsPerIP: 100,
connectionRateLimitPerMinute: 600,
socketTimeout: 120_000,
inactivityTimeout: 120_000,
keepAlive: true,
noDelay: true,
gracefulShutdownTimeout: 30_000,
// --- user overrides ---
...this.options.smartProxyConfig,
// --- deep-merge defaults.security so user can override maxConnections ---
defaults: {
...this.options.smartProxyConfig?.defaults,
security: {
maxConnections: 50_000,
...this.options.smartProxyConfig?.defaults?.security,
},
},
// --- always set by dcrouter (after spread) ---
routes,
acme: acmeConfig,
certStore: {

View File

@@ -47,13 +47,13 @@ import * as qenv from '@push.rocks/qenv';
import * as smartacme from '@push.rocks/smartacme';
import * as smartdata from '@push.rocks/smartdata';
import * as smartdns from '@push.rocks/smartdns';
import * as smartfile from '@push.rocks/smartfile';
import * as smartfs from '@push.rocks/smartfs';
import * as smartguard from '@push.rocks/smartguard';
import * as smartjwt from '@push.rocks/smartjwt';
import * as smartlog from '@push.rocks/smartlog';
import * as smartmetrics from '@push.rocks/smartmetrics';
import * as smartmta from '@push.rocks/smartmta';
import * as smartmongo from '@push.rocks/smartmongo';
import * as smartdb from '@push.rocks/smartdb';
import * as smartnetwork from '@push.rocks/smartnetwork';
import * as smartpath from '@push.rocks/smartpath';
import * as smartproxy from '@push.rocks/smartproxy';
@@ -64,7 +64,7 @@ import * as smartrx from '@push.rocks/smartrx';
import * as smartunique from '@push.rocks/smartunique';
import * as taskbuffer from '@push.rocks/taskbuffer';
export { projectinfo, qenv, smartacme, smartdata, smartdns, smartfile, smartguard, smartjwt, smartlog, smartmetrics, smartmongo, smartmta, smartnetwork, smartpath, smartproxy, smartpromise, smartradius, smartrequest, smartrx, smartunique, taskbuffer };
export { projectinfo, qenv, smartacme, smartdata, smartdns, smartfs, smartguard, smartjwt, smartlog, smartmetrics, smartdb, smartmta, smartnetwork, smartpath, smartproxy, smartpromise, smartradius, smartrequest, smartrx, smartunique, taskbuffer };
// Define SmartLog types for use in error handling
export type TLogLevel = 'error' | 'warn' | 'info' | 'success' | 'debug';
@@ -90,7 +90,7 @@ export {
uuid,
}
// Filesystem utilities (compatibility helpers for smartfile v13+)
// Filesystem utilities
export const fsUtils = {
/**
* Ensure a directory exists, creating it recursively if needed (sync)

View File

@@ -96,5 +96,27 @@ export function getOciContainerConfig(): IDcRouterOptions {
};
}
// Connection capacity config
const maxConnections = process.env.DCROUTER_MAX_CONNECTIONS;
const maxConnectionsPerIP = process.env.DCROUTER_MAX_CONNECTIONS_PER_IP;
const connectionRateLimit = process.env.DCROUTER_CONNECTION_RATE_LIMIT;
if (maxConnections || maxConnectionsPerIP || connectionRateLimit) {
options.smartProxyConfig = {
...options.smartProxyConfig,
routes: options.smartProxyConfig?.routes || [],
...(maxConnectionsPerIP ? { maxConnectionsPerIP: parseInt(maxConnectionsPerIP, 10) } : {}),
...(connectionRateLimit ? { connectionRateLimitPerMinute: parseInt(connectionRateLimit, 10) } : {}),
...(maxConnections ? {
defaults: {
...options.smartProxyConfig?.defaults,
security: {
...options.smartProxyConfig?.defaults?.security,
maxConnections: parseInt(maxConnections, 10),
},
},
} : {}),
};
}
return options;
}

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@serve.zone/dcrouter',
version: '11.10.7',
version: '11.11.0',
description: 'A multifaceted routing service handling mail and SMS delivery functions.'
}