@ossy/deployment-tools 1.21.0 → 1.21.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,22 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## 1.21.2 (2026-05-26)
7
+
8
+ **Note:** Version bump only for package @ossy/deployment-tools
9
+
10
+
11
+
12
+
13
+
14
+ ## 1.21.1 (2026-05-26)
15
+
16
+ **Note:** Version bump only for package @ossy/deployment-tools
17
+
18
+
19
+
20
+
21
+
6
22
  # 1.21.0 (2026-05-26)
7
23
 
8
24
 
package/README.md CHANGED
@@ -75,7 +75,9 @@ Host ports are auto-assigned starting at `3002`, counting only HTTP services (TC
75
75
 
76
76
  #### TCP/UDP service
77
77
 
78
- No Caddy block. Ports are mapped directly (`HOST:CONTAINER`) and opened in the EC2 security group. Use this for game servers or any raw socket protocol.
78
+ No Caddy block. Containers run with **`--network host`** so game servers bind ports on the EC2 instance directly (avoids Docker bridge DNAT issues for public traffic). The `ports` and `protocol` fields still drive **security group** ingress rules they are not passed as `-p` mappings.
79
+
80
+ Use this for game servers or any other raw socket protocol.
79
81
 
80
82
  ```json
81
83
  "services": [
@@ -85,6 +87,15 @@ No Caddy block. Ports are mapped directly (`HOST:CONTAINER`) and opened in the E
85
87
  "image": "itzg/minecraft-server",
86
88
  "ports": [25565],
87
89
  "protocol": "tcp"
90
+ },
91
+ {
92
+ "name": "palworld",
93
+ "type": "tcp",
94
+ "image": "thijsvanloef/palworld-server-docker",
95
+ "ports": [
96
+ { "port": 8211, "protocol": "udp" },
97
+ { "port": 27021, "protocol": "udp" }
98
+ ]
88
99
  }
89
100
  ]
90
101
  ```
@@ -94,8 +105,8 @@ No Caddy block. Ports are mapped directly (`HOST:CONTAINER`) and opened in the E
94
105
  | `name` | Unique service name — Docker container name and systemd unit. |
95
106
  | `type` | Must be `"tcp"` to enable this mode. |
96
107
  | `image` | Docker image to pull and run. |
97
- | `ports` | Array of port numbers to expose on the host and open in the security group. |
98
- | `protocol` | `"tcp"` (default), `"udp"`, or `"both"`. |
108
+ | `ports` | Port numbers (host network) and security group rules. Use a **number** with service-level `protocol`, or `{ "port": 8211, "protocol": "udp" }` per port when protocols differ. |
109
+ | `protocol` | Default for numeric `ports` entries: `"tcp"` (default), `"udp"`, or `"both"`. Ignored when every port is an object with its own `protocol`. Must match what the game image listens on. |
99
110
 
100
111
  **Fixed built-in services** (always present, not configurable via `services`):
101
112
  - `ossy-api` — the Ossy API (`ghcr.io/ossy-se/api:latest`, port 3001)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ossy/deployment-tools",
3
- "version": "1.21.0",
3
+ "version": "1.21.2",
4
4
  "description": "Collection of scripts and tools to aid deployment of containers and static files to Amazon Web Services through GitHub Actions",
5
5
  "source": "./src/index.js",
6
6
  "main": "./src/index.js",
@@ -22,5 +22,5 @@
22
22
  "devDependencies": {
23
23
  "jest": "^27.5.1"
24
24
  },
25
- "gitHead": "6eb721d6fa63b333f4b8f8e9b4178f721ecaa531"
25
+ "gitHead": "5747c957d77061c1172a0ac7a6a54d1644986927"
26
26
  }
@@ -17,8 +17,8 @@
17
17
  * @property {'tcp'} type - marks a raw socket service; bypasses Caddy entirely
18
18
  * @property {string} name - unique service name (container + systemd unit)
19
19
  * @property {string} image - Docker image (e.g. itzg/minecraft-server)
20
- * @property {number[]} ports - ports to open on the host and in the EC2 security group
21
- * @property {'tcp'|'udp'|'both'} [protocol] - defaults to 'tcp'
20
+ * @property {(number|{port: number, protocol: 'tcp'|'udp'|'both'})[]} ports - host ports + security group rules
21
+ * @property {'tcp'|'udp'|'both'} [protocol] - default for numeric ports; defaults to 'tcp'
22
22
  * @property {Record<string,string>=} env - environment variables written to /etc/environment on the EC2 host at boot time; never uploaded to S3
23
23
  *
24
24
  * @property {string} awsAccountId - Aws account id
@@ -11,14 +11,38 @@
11
11
  * @property {'tcp'} type - marks this as a raw TCP/UDP service (no HTTP/Caddy)
12
12
  * @property {string} name - unique name, used for container name and systemd unit
13
13
  * @property {string} image - Docker image to run
14
- * @property {number[]} ports - ports to expose directly (same inside and outside container)
15
- * @property {'tcp'|'udp'|'both'} [protocol] - defaults to 'tcp'
14
+ * @property {(number|TcpPortEntry)[]} ports - host ports; each entry uses `protocol` or the service default
15
+ * @property {'tcp'|'udp'|'both'} [protocol] - default for numeric ports; defaults to 'tcp'
16
+ *
17
+ * @typedef {Object} TcpPortEntry
18
+ * @property {number} port
19
+ * @property {'tcp'|'udp'|'both'} protocol
16
20
  *
17
21
  * @typedef {HttpServiceConfig|TcpServiceConfig} ContainerServiceConfig
18
22
  */
19
23
 
20
24
  const BASE_HTTP_PORT = 3002 // 3000 = platform runtime, 3001 = API
21
25
 
26
+ /**
27
+ * @param {(number|TcpPortEntry)[]} ports
28
+ * @param {'tcp'|'udp'|'both'} defaultProtocol
29
+ * @returns {TcpPortEntry[]}
30
+ */
31
+ const normalizePortEntries = (ports, defaultProtocol = 'tcp') =>
32
+ ports.map(entry =>
33
+ typeof entry === 'number'
34
+ ? { port: entry, protocol: defaultProtocol }
35
+ : { port: entry.port, protocol: entry.protocol ?? defaultProtocol }
36
+ )
37
+
38
+ /** @param {TcpPortEntry} entry */
39
+ const expandSecurityGroupRules = ({ port, protocol }) => {
40
+ if (protocol === 'both') {
41
+ return [{ port, protocol: 'tcp' }, { port, protocol: 'udp' }]
42
+ }
43
+ return [{ port, protocol }]
44
+ }
45
+
22
46
  // ---------------------------------------------------------------------------
23
47
  // HTTP service — reverse-proxied through Caddy, container listens on port 3000
24
48
  // ---------------------------------------------------------------------------
@@ -80,23 +104,23 @@ ${this.domain} {
80
104
  }
81
105
 
82
106
  // ---------------------------------------------------------------------------
83
- // TCP/UDP service — bypasses Caddy, ports exposed directly on the host.
84
- // Used for game servers and any other raw socket services.
107
+ // TCP/UDP service — bypasses Caddy, listens on the EC2 host network namespace.
108
+ // Uses --network host so game traffic hits the process directly (no bridge DNAT).
109
+ // Security group ingress still comes from securityGroupPorts below.
85
110
  // ---------------------------------------------------------------------------
86
111
 
87
112
  class TcpContainerService {
88
113
  constructor({ name, image, ports, protocol = 'tcp' }) {
89
114
  this.name = name
90
115
  this.image = image
91
- this.ports = ports
92
- this.protocol = protocol // 'tcp' | 'udp' | 'both'
116
+ this.portEntries = normalizePortEntries(ports, protocol)
93
117
  }
94
118
 
95
119
  get systemdUnitFile() {
96
- const portMappings = this.ports.map(p => `-p ${p}:${p}`).join(' \\\n ')
120
+ const portList = this.portEntries.map(({ port, protocol }) => `${port}/${protocol}`).join(', ')
97
121
  return `
98
122
  [Unit]
99
- Description=Container service: ${this.name} (${this.protocol.toUpperCase()} ${this.ports.join(', ')})
123
+ Description=Container service: ${this.name} (host network, ${portList})
100
124
  After=network.target docker.service
101
125
  Requires=docker.service
102
126
 
@@ -105,8 +129,7 @@ EnvironmentFile=/etc/environment
105
129
  ExecStartPre=-/usr/bin/docker rm -f ${this.name}
106
130
  ExecStartPre=/usr/bin/docker pull ${this.image}
107
131
  ExecStart=/usr/bin/docker run --name ${this.name} \\
108
- --network ossy-network \\
109
- ${portMappings} \\
132
+ --network host \\
110
133
  --env-file /etc/environment \\
111
134
  ${this.image}
112
135
  ExecStop=/usr/bin/docker stop ${this.name}
@@ -121,12 +144,9 @@ WantedBy=multi-user.target
121
144
  /** TCP services bypass Caddy — no Caddy block. */
122
145
  get caddyBlock() { return null }
123
146
 
124
- /** Each port needs an explicit security group ingress rule. */
147
+ /** Each port needs an explicit security group ingress rule matching its protocol. */
125
148
  get securityGroupPorts() {
126
- return this.ports.flatMap(port => {
127
- if (this.protocol === 'both') return [{ port, protocol: 'tcp' }, { port, protocol: 'udp' }]
128
- return [{ port, protocol: this.protocol }]
129
- })
149
+ return this.portEntries.flatMap(expandSecurityGroupRules)
130
150
  }
131
151
 
132
152
  get unitName() { return `${this.name}.service` }
@@ -158,5 +178,6 @@ const fromConfig = (services = []) => {
158
178
  module.exports = {
159
179
  HttpContainerService,
160
180
  TcpContainerService,
181
+ normalizePortEntries,
161
182
  fromConfig
162
183
  }