├── .github ├── dependabot.yml ├── docker-image-built-against └── workflows │ ├── build-and-push.yml │ └── check-for-updates.yml ├── .gitignore ├── Dockerfile ├── FUNDING.yml ├── LICENSE ├── README.md ├── docker-wireguard-tunnel.excalidraw ├── docker-wireguard-tunnel.png ├── example-tls-fly-io.md ├── example-tls-traefik.md └── wg-start.sh /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "docker" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | 13 | # Maintain dependencies for GitHub Actions 14 | - package-ecosystem: "github-actions" 15 | directory: "/" 16 | schedule: 17 | interval: "daily" 18 | -------------------------------------------------------------------------------- /.github/docker-image-built-against: -------------------------------------------------------------------------------- 1 | cea2ff433c61 2 | -------------------------------------------------------------------------------- /.github/workflows/build-and-push.yml: -------------------------------------------------------------------------------- 1 | name: Build and push 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build-and-push: 11 | runs-on: ubuntu-latest 12 | 13 | permissions: 14 | # Give the default GITHUB_TOKEN write permission to commit and push the 15 | # added or changed files to the repository. 16 | contents: write 17 | 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v4 21 | 22 | - name: Get latest release tag 23 | id: get-latest-tag 24 | run: | 25 | git fetch --tags 26 | latest_tag=$(git describe --tags `git rev-list --tags --max-count=1` 2>/dev/null || echo "v0.0.0") 27 | echo "tag=$latest_tag" >> $GITHUB_ENV 28 | 29 | - name: Get current date 30 | id: date 31 | run: echo "date=$(date +'%Y.%m.%d')" >> $GITHUB_ENV 32 | 33 | - name: Save image build info 34 | run: | 35 | docker pull alpine:3.22 36 | docker images --format '{{.ID}}' alpine:3.22 > .github/docker-image-built-against 37 | 38 | - name: Set up QEMU 39 | uses: docker/setup-qemu-action@v3 40 | 41 | - name: Set up Docker Buildx 42 | uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 43 | with: 44 | driver-opts: 'image=moby/buildkit:v0.10.5' 45 | 46 | - name: Login to GitHub Container Registry 47 | uses: docker/login-action@v3 48 | with: 49 | registry: ghcr.io 50 | username: ${{ secrets.GHCR_USERNAME }} 51 | password: ${{ secrets.GHCR_TOKEN }} 52 | 53 | - name: Build and push 54 | uses: docker/build-push-action@v6 55 | with: 56 | context: . 57 | platforms: linux/amd64,linux/arm64,linux/arm/v7 58 | push: true 59 | tags: | 60 | ghcr.io/digitallyrefined/docker-wireguard-tunnel:latest 61 | ghcr.io/digitallyrefined/docker-wireguard-tunnel:${{ env.tag }} 62 | ghcr.io/digitallyrefined/docker-wireguard-tunnel:${{ env.tag }}-${{ env.date }} 63 | 64 | - name: Commit and push changes 65 | uses: stefanzweifel/git-auto-commit-action@v5 66 | with: 67 | commit_message: Update image built against version 68 | -------------------------------------------------------------------------------- /.github/workflows/check-for-updates.yml: -------------------------------------------------------------------------------- 1 | name: Check for updates 2 | 3 | on: 4 | schedule: 5 | - cron: '0 4 * * *' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | check-for-updates: 10 | runs-on: ubuntu-latest 11 | 12 | permissions: 13 | # Give the default GITHUB_TOKEN write permission to commit and push the 14 | # added or changed files to the repository. 15 | contents: write 16 | 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | 21 | - name: Check if image needs updating 22 | id: image-update 23 | run: | 24 | docker pull alpine:3.22 25 | docker images --format '{{.ID}}' alpine:3.22 > .github/docker-image-built-against 26 | img_needs_updating=`git status --porcelain` 27 | set -x 28 | echo "needs-updating=`[[ $img_needs_updating ]] && echo true || echo false`" >>$GITHUB_OUTPUT 29 | 30 | - name: Check if there are any package updates 31 | id: pkg-update 32 | run: | 33 | docker pull ghcr.io/digitallyrefined/docker-wireguard-tunnel 34 | pkgs_to_update=`docker run --rm ghcr.io/digitallyrefined/docker-wireguard-tunnel sh -c ' \ 35 | apk upgrade --simulate --no-cache | { grep Upgrading || true; }'` 36 | set -x 37 | echo "needs-updating=`[[ $pkgs_to_update ]] && echo true || echo false`" >>$GITHUB_OUTPUT 38 | 39 | - name: Get latest release tag 40 | id: get-latest-tag 41 | run: | 42 | git fetch --tags 43 | latest_tag=$(git describe --tags `git rev-list --tags --max-count=1` 2>/dev/null || echo "v0.0.0") 44 | echo "tag=$latest_tag" >> $GITHUB_ENV 45 | if: steps.image-update.outputs.needs-updating == 'true' || steps.pkg-update.outputs.needs-updating == 'true' 46 | 47 | - name: Get current date 48 | id: date 49 | run: echo "date=$(date +'%Y.%m.%d')" >> $GITHUB_ENV 50 | if: steps.image-update.outputs.needs-updating == 'true' || steps.pkg-update.outputs.needs-updating == 'true' 51 | 52 | - name: Set up QEMU 53 | uses: docker/setup-qemu-action@v3 54 | if: steps.image-update.outputs.needs-updating == 'true' || steps.pkg-update.outputs.needs-updating == 'true' 55 | 56 | - name: Set up Docker Buildx 57 | uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 58 | with: 59 | driver-opts: 'image=moby/buildkit:v0.10.5' 60 | if: steps.image-update.outputs.needs-updating == 'true' || steps.pkg-update.outputs.needs-updating == 'true' 61 | 62 | - name: Login to GitHub Container Registry 63 | uses: docker/login-action@v3 64 | with: 65 | registry: ghcr.io 66 | username: ${{ secrets.GHCR_USERNAME }} 67 | password: ${{ secrets.GHCR_TOKEN }} 68 | if: steps.image-update.outputs.needs-updating == 'true' || steps.pkg-update.outputs.needs-updating == 'true' 69 | 70 | - name: Build and push 71 | uses: docker/build-push-action@v6 72 | with: 73 | context: . 74 | platforms: linux/amd64,linux/arm64,linux/arm/v7 75 | push: true 76 | tags: | 77 | ghcr.io/digitallyrefined/docker-wireguard-tunnel:latest 78 | ghcr.io/digitallyrefined/docker-wireguard-tunnel:${{ env.tag }} 79 | ghcr.io/digitallyrefined/docker-wireguard-tunnel:${{ env.tag }}-${{ env.date }} 80 | if: steps.image-update.outputs.needs-updating == 'true' || steps.pkg-update.outputs.needs-updating == 'true' 81 | 82 | - name: Commit and push changes 83 | uses: stefanzweifel/git-auto-commit-action@v5 84 | with: 85 | commit_message: Update image built against version 86 | if: steps.image-update.outputs.needs-updating == 'true' || steps.pkg-update.outputs.needs-updating == 'true' 87 | 88 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | config 2 | peers -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM alpine:3.22 4 | 5 | ARG TARGETPLATFORM 6 | 7 | LABEL org.opencontainers.image.source=https://github.com/DigitallyRefined/docker-wireguard-tunnel 8 | LABEL org.opencontainers.image.description="docker-wireguard-tunnel ${TARGETPLATFORM}" 9 | 10 | RUN apk upgrade --no-cache 11 | RUN apk add --no-cache wireguard-tools 12 | RUN apk add --no-cache rinetd --repository=https://dl-cdn.alpinelinux.org/alpine/edge/testing 13 | RUN cp /etc/rinetd.conf /etc/rinetd.conf.ori 14 | 15 | COPY wg-start.sh /usr/sbin/wg-start.sh 16 | 17 | CMD ["/usr/sbin/wg-start.sh"] 18 | 19 | EXPOSE 51820/udp 20 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: DigitallyRefined 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright 2023 DigitallyRefined 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Docker WireGuard Tunnel 2 | 3 | [![Build and push workflow](https://github.com/DigitallyRefined/docker-wireguard-tunnel/actions/workflows/build-and-push.yml/badge.svg)](https://github.com/DigitallyRefined/docker-wireguard-tunnel/actions/workflows/build-and-push.yml) 4 | [![Check for updates workflow](https://github.com/DigitallyRefined/docker-wireguard-tunnel/actions/workflows/check-for-updates.yml/badge.svg)](https://github.com/DigitallyRefined/docker-wireguard-tunnel/actions/workflows/check-for-updates.yml) 5 | [![Container registry](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fipitio.github.io%2Fbackage%2FDigitallyRefined%2Fdocker-wireguard-tunnel%2Fdocker-wireguard-tunnel.json&query=%24.downloads&label=Pulls)](https://github.com/DigitallyRefined/docker-wireguard-tunnel/pkgs/container/docker-wireguard-tunnel) 6 | [![Container registry](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fipitio.github.io%2Fbackage%2FDigitallyRefined%2Fdocker-wireguard-tunnel%2Fdocker-wireguard-tunnel.json&query=%24.downloads_month&label=Pulls%20per%20month)](https://github.com/DigitallyRefined/docker-wireguard-tunnel/pkgs/container/docker-wireguard-tunnel) 7 | 8 | Connect two or more Docker servers together sharing container ports between them via a [WireGuard](https://www.wireguard.com/) tunnel. 9 | 10 | For example a Docker server without a public IP address behind a NAT can expose container ports to another Docker server that has a public IP address to allow incoming connections. 11 | 12 | ![Example Topology](docker-wireguard-tunnel.png) 13 | 14 | ## Usage Example 15 | 16 | This assumes that you have already setup a subdomain DNS entry for your domain, for example: 17 | `wireguard-tunnel.example.com` 18 | 19 | ### Server 20 | 21 | Will accept connections on behalf of a peer and tunnel them to the designated peer. 22 | 23 | `docker-compose.yml` 24 | 25 | ```yml 26 | services: 27 | wireguard-tunnel-server: 28 | image: ghcr.io/digitallyrefined/docker-wireguard-tunnel:v3 29 | container_name: wireguard-tunnel-server 30 | environment: 31 | # Update to your domain 32 | - DOMAIN=wireguard-tunnel.example.com 33 | # Number of peers to auto generate config for 34 | - PEERS=1 35 | # Services to expose format (comma-separated) 36 | # SERVICES=peer-id:peer-container-name:peer-container-port:expose-port-as 37 | - SERVICES=peer1:nginx:80:8080,peer1:nginx-demo:80:8081 38 | cap_add: 39 | - NET_ADMIN 40 | volumes: 41 | - ./config:/etc/wireguard 42 | restart: unless-stopped 43 | ports: 44 | - '51820:51820/udp' 45 | - 8080:8080 46 | - 8081:8081 47 | ``` 48 | 49 | ```bash 50 | docker compose up -d 51 | docker compose logs -f 52 | ``` 53 | 54 | Once started, a `peer1.conf` file will be automatically generated in the `config` directory. 55 | 56 | ### Peer 57 | 58 | Will connect to the server via WireGuard and setup a tunnel to expose the listed ports. 59 | 60 | Move the `config/peer1.conf` file from the server that was automatically generated and rename it to `config/wg0.conf` on the peer. 61 | 62 | `docker-compose.yml` 63 | 64 | ```yml 65 | services: 66 | wireguard-tunnel-peer: 67 | image: ghcr.io/digitallyrefined/docker-wireguard-tunnel:v3 68 | container_name: wireguard-tunnel-peer 69 | environment: 70 | # Note that DOMAIN & PEERS are not required for the peer 71 | # Services to expose format (comma-separated) 72 | # SERVICES=peer-id:peer-container-name:peer-container-port:expose-port-as 73 | - SERVICES=peer1:nginx:80:8080,peer1:nginx-demo:80:8081 74 | cap_add: 75 | - NET_ADMIN 76 | volumes: 77 | - ./config:/etc/wireguard 78 | restart: unless-stopped 79 | links: 80 | - nginx:nginx 81 | - nginx-demo:nginx-demo 82 | 83 | nginx: 84 | image: nginx 85 | restart: unless-stopped 86 | 87 | nginx-demo: 88 | image: nginxdemos/hello 89 | restart: unless-stopped 90 | ``` 91 | 92 | ```bash 93 | docker compose up -d 94 | docker compose logs -f 95 | ``` 96 | 97 | Note: if you have a firewall in front of your server you will need to allow connections on port `51820/udp` for the WireGuard server, and connections on ports `8080` and `8081` for the 2 demo nginx servers. 98 | 99 | Once started you should be able to access both nginx servers via their exposed ports on the WireGuard server, for example: 100 | `wireguard-tunnel.example.com:8080` and `wireguard-tunnel.example.com:8081` 101 | 102 | You may want to combine the WireGuard tunnel server with [Traefik](example-tls-traefik.md) or [Nginx Proxy Manager](https://nginxproxymanager.com/) or use a 3rd party service such as [Fly.io](example-tls-fly-io.md). 103 | 104 | Examples using Docker WireGuard Tunnel with: 105 | 106 | * [Traefik](example-tls-traefik.md) to automatically provision TLS/HTTPS certificates 107 | * [Fly.io](example-tls-fly-io.md) to provision a server, subdomain and TLS/HTTPS certificates 108 | -------------------------------------------------------------------------------- /docker-wireguard-tunnel.excalidraw: -------------------------------------------------------------------------------- 1 | { 2 | "type": "excalidraw", 3 | "version": 2, 4 | "source": "https://excalidraw.com", 5 | "elements": [ 6 | { 7 | "type": "line", 8 | "version": 976, 9 | "versionNonce": 193493904, 10 | "isDeleted": false, 11 | "id": "NJCk3YzUTq_aUmccAHt3s", 12 | "fillStyle": "solid", 13 | "strokeWidth": 2, 14 | "strokeStyle": "solid", 15 | "roughness": 0, 16 | "opacity": 100, 17 | "angle": 0, 18 | "x": 1212.7271865479843, 19 | "y": 598.9611830417575, 20 | "strokeColor": "#495057", 21 | "backgroundColor": "#ced4da", 22 | "width": 132.9465899756076, 23 | "height": 230.69394714407466, 24 | "seed": 358174064, 25 | "groupIds": [ 26 | "TmclqM7Qu4H0ux2YfQlc-" 27 | ], 28 | "roundness": null, 29 | "boundElements": [], 30 | "updated": 1682873021995, 31 | "link": null, 32 | "locked": false, 33 | "startBinding": null, 34 | "endBinding": null, 35 | "lastCommittedPoint": null, 36 | "startArrowhead": null, 37 | "endArrowhead": null, 38 | "points": [ 39 | [ 40 | 0, 41 | 0 42 | ], 43 | [ 44 | 96.50085028033065, 45 | 54.885790530279856 46 | ], 47 | [ 48 | 131.63643167190486, 49 | 33.90204053253383 50 | ], 51 | [ 52 | 132.82459142910807, 53 | -118.76587969879068 54 | ], 55 | [ 56 | 35.013582845075796, 57 | -175.8081566137948 58 | ], 59 | [ 60 | -0.12199854649954328, 61 | -153.3604240580667 62 | ], 63 | [ 64 | 0, 65 | 0 66 | ] 67 | ] 68 | }, 69 | { 70 | "type": "line", 71 | "version": 785, 72 | "versionNonce": 737854832, 73 | "isDeleted": false, 74 | "id": "v3hFrkyKdJuKzoeVWZM1X", 75 | "fillStyle": "solid", 76 | "strokeWidth": 2, 77 | "strokeStyle": "solid", 78 | "roughness": 0, 79 | "opacity": 100, 80 | "angle": 0, 81 | "x": 1212.8523453018588, 82 | "y": 445.83907204514094, 83 | "strokeColor": "#495057", 84 | "backgroundColor": "#ced4da", 85 | "width": 98.38420284190222, 86 | "height": 206.98047966842483, 87 | "seed": 1504583536, 88 | "groupIds": [ 89 | "TmclqM7Qu4H0ux2YfQlc-" 90 | ], 91 | "roundness": null, 92 | "boundElements": [], 93 | "updated": 1682873021995, 94 | "link": null, 95 | "locked": false, 96 | "startBinding": null, 97 | "endBinding": null, 98 | "lastCommittedPoint": null, 99 | "startArrowhead": null, 100 | "endArrowhead": null, 101 | "points": [ 102 | [ 103 | 0, 104 | 0 105 | ], 106 | [ 107 | 98.38420284190222, 108 | 57.941684677642016 109 | ], 110 | [ 111 | 97.30212877730584, 112 | 206.98047966842483 113 | ] 114 | ] 115 | }, 116 | { 117 | "type": "line", 118 | "version": 684, 119 | "versionNonce": 974293392, 120 | "isDeleted": false, 121 | "id": "hDT6ylN5jMLNBuOPWeUwD", 122 | "fillStyle": "solid", 123 | "strokeWidth": 2, 124 | "strokeStyle": "solid", 125 | "roughness": 0, 126 | "opacity": 100, 127 | "angle": 0, 128 | "x": 1311.7775851760591, 129 | "y": 503.15518265418905, 130 | "strokeColor": "#495057", 131 | "backgroundColor": "#ced4da", 132 | "width": 33.2995835289743, 133 | "height": 21.183201568830203, 134 | "seed": 530100592, 135 | "groupIds": [ 136 | "TmclqM7Qu4H0ux2YfQlc-" 137 | ], 138 | "roundness": null, 139 | "boundElements": [], 140 | "updated": 1682873021995, 141 | "link": null, 142 | "locked": false, 143 | "startBinding": null, 144 | "endBinding": null, 145 | "lastCommittedPoint": null, 146 | "startArrowhead": null, 147 | "endArrowhead": null, 148 | "points": [ 149 | [ 150 | 0, 151 | 0 152 | ], 153 | [ 154 | 33.2995835289743, 155 | -21.183201568830203 156 | ] 157 | ] 158 | }, 159 | { 160 | "type": "line", 161 | "version": 1044, 162 | "versionNonce": 224434032, 163 | "isDeleted": false, 164 | "id": "rHueNF3lPuYu27IsL8fZZ", 165 | "fillStyle": "solid", 166 | "strokeWidth": 2, 167 | "strokeStyle": "solid", 168 | "roughness": 0, 169 | "opacity": 100, 170 | "angle": 0, 171 | "x": 1212.2116435530843, 172 | "y": 575.5900748072163, 173 | "strokeColor": "#495057", 174 | "backgroundColor": "#ced4da", 175 | "width": 130.882036074061, 176 | "height": 57.286745112229376, 177 | "seed": 778109808, 178 | "groupIds": [ 179 | "TmclqM7Qu4H0ux2YfQlc-" 180 | ], 181 | "roundness": null, 182 | "boundElements": [], 183 | "updated": 1682873021995, 184 | "link": null, 185 | "locked": false, 186 | "startBinding": null, 187 | "endBinding": null, 188 | "lastCommittedPoint": null, 189 | "startArrowhead": null, 190 | "endArrowhead": null, 191 | "points": [ 192 | [ 193 | 0, 194 | 0 195 | ], 196 | [ 197 | 97.40179349378211, 198 | 57.286745112229376 199 | ], 200 | [ 201 | 130.882036074061, 202 | 35.823512454989384 203 | ] 204 | ] 205 | }, 206 | { 207 | "type": "line", 208 | "version": 1085, 209 | "versionNonce": 1747638160, 210 | "isDeleted": false, 211 | "id": "UssvB76ItF4CQCAbqotr1", 212 | "fillStyle": "solid", 213 | "strokeWidth": 2, 214 | "strokeStyle": "solid", 215 | "roughness": 0, 216 | "opacity": 100, 217 | "angle": 0, 218 | "x": 1214.0483218995696, 219 | "y": 526.7258881007392, 220 | "strokeColor": "#495057", 221 | "backgroundColor": "#ced4da", 222 | "width": 131.15255459021012, 223 | "height": 57.286745112229376, 224 | "seed": 609162608, 225 | "groupIds": [ 226 | "TmclqM7Qu4H0ux2YfQlc-" 227 | ], 228 | "roundness": null, 229 | "boundElements": [], 230 | "updated": 1682873021995, 231 | "link": null, 232 | "locked": false, 233 | "startBinding": null, 234 | "endBinding": null, 235 | "lastCommittedPoint": null, 236 | "startArrowhead": null, 237 | "endArrowhead": null, 238 | "points": [ 239 | [ 240 | 0, 241 | 0 242 | ], 243 | [ 244 | 97.40179349378212, 245 | 57.286745112229376 246 | ], 247 | [ 248 | 131.15255459021012, 249 | 35.823512454989384 250 | ] 251 | ] 252 | }, 253 | { 254 | "type": "line", 255 | "version": 1108, 256 | "versionNonce": 641476976, 257 | "isDeleted": false, 258 | "id": "WWWlAavR2pPjYQ0XBn93q", 259 | "fillStyle": "solid", 260 | "strokeWidth": 2, 261 | "strokeStyle": "solid", 262 | "roughness": 0, 263 | "opacity": 100, 264 | "angle": 0, 265 | "x": 1212.7526805853824, 266 | "y": 499.7025121191299, 267 | "strokeColor": "#495057", 268 | "backgroundColor": "#ced4da", 269 | "width": 130.34099904176284, 270 | "height": 57.286745112229376, 271 | "seed": 380299120, 272 | "groupIds": [ 273 | "TmclqM7Qu4H0ux2YfQlc-" 274 | ], 275 | "roundness": null, 276 | "boundElements": [], 277 | "updated": 1682873021995, 278 | "link": null, 279 | "locked": false, 280 | "startBinding": null, 281 | "endBinding": null, 282 | "lastCommittedPoint": null, 283 | "startArrowhead": null, 284 | "endArrowhead": null, 285 | "points": [ 286 | [ 287 | 0, 288 | 0 289 | ], 290 | [ 291 | 97.40179349378214, 292 | 57.286745112229376 293 | ], 294 | [ 295 | 130.34099904176284, 296 | 36.364549487287576 297 | ] 298 | ] 299 | }, 300 | { 301 | "type": "line", 302 | "version": 1113, 303 | "versionNonce": 168387984, 304 | "isDeleted": false, 305 | "id": "BQvDKtwHOwqWcKI3DnpuO", 306 | "fillStyle": "solid", 307 | "strokeWidth": 2, 308 | "strokeStyle": "solid", 309 | "roughness": 0, 310 | "opacity": 100, 311 | "angle": 0, 312 | "x": 1212.7526805853824, 313 | "y": 474.08583242149405, 314 | "strokeColor": "#495057", 315 | "backgroundColor": "#ced4da", 316 | "width": 130.61151755791192, 317 | "height": 57.286745112229376, 318 | "seed": 834435440, 319 | "groupIds": [ 320 | "TmclqM7Qu4H0ux2YfQlc-" 321 | ], 322 | "roundness": null, 323 | "boundElements": [], 324 | "updated": 1682873021995, 325 | "link": null, 326 | "locked": false, 327 | "startBinding": null, 328 | "endBinding": null, 329 | "lastCommittedPoint": null, 330 | "startArrowhead": null, 331 | "endArrowhead": null, 332 | "points": [ 333 | [ 334 | 0, 335 | 0 336 | ], 337 | [ 338 | 97.40179349378212, 339 | 57.286745112229376 340 | ], 341 | [ 342 | 130.61151755791192, 343 | 36.09403097113848 344 | ] 345 | ] 346 | }, 347 | { 348 | "type": "line", 349 | "version": 736, 350 | "versionNonce": 30767984, 351 | "isDeleted": false, 352 | "id": "KGBVBtHfsuMLFRzf1llsL", 353 | "fillStyle": "solid", 354 | "strokeWidth": 2, 355 | "strokeStyle": "solid", 356 | "roughness": 0, 357 | "opacity": 100, 358 | "angle": 0, 359 | "x": 1244.3734653116135, 360 | "y": 489.6532995193137, 361 | "strokeColor": "#495057", 362 | "backgroundColor": "#ced4da", 363 | "width": 34.58968959029347, 364 | "height": 46.18989036752532, 365 | "seed": 1489750896, 366 | "groupIds": [ 367 | "TmclqM7Qu4H0ux2YfQlc-" 368 | ], 369 | "roundness": null, 370 | "boundElements": [], 371 | "updated": 1682873021995, 372 | "link": null, 373 | "locked": false, 374 | "startBinding": null, 375 | "endBinding": null, 376 | "lastCommittedPoint": null, 377 | "startArrowhead": null, 378 | "endArrowhead": null, 379 | "points": [ 380 | [ 381 | 0, 382 | 0 383 | ], 384 | [ 385 | -0.3026139333192493, 386 | -26.36409267552797 387 | ], 388 | [ 389 | 34.28707565697422, 390 | -46.18989036752532 391 | ] 392 | ] 393 | }, 394 | { 395 | "type": "line", 396 | "version": 717, 397 | "versionNonce": 1290154896, 398 | "isDeleted": false, 399 | "id": "eKIxSsXoumWPR9ZPf_dKl", 400 | "fillStyle": "solid", 401 | "strokeWidth": 2, 402 | "strokeStyle": "solid", 403 | "roughness": 0, 404 | "opacity": 100, 405 | "angle": 0, 406 | "x": 1286.8953080025453, 407 | "y": 515.1920814676077, 408 | "strokeColor": "#495057", 409 | "backgroundColor": "#ced4da", 410 | "width": 35.49753139024895, 411 | "height": 48.11561539774027, 412 | "seed": 1176558960, 413 | "groupIds": [ 414 | "TmclqM7Qu4H0ux2YfQlc-" 415 | ], 416 | "roundness": null, 417 | "boundElements": [], 418 | "updated": 1682873021995, 419 | "link": null, 420 | "locked": false, 421 | "startBinding": null, 422 | "endBinding": null, 423 | "lastCommittedPoint": null, 424 | "startArrowhead": null, 425 | "endArrowhead": null, 426 | "points": [ 427 | [ 428 | 0, 429 | 0 430 | ], 431 | [ 432 | -0.4218254828087191, 433 | -26.72172732399638 434 | ], 435 | [ 436 | 35.07570590744023, 437 | -48.11561539774027 438 | ] 439 | ] 440 | }, 441 | { 442 | "type": "line", 443 | "version": 680, 444 | "versionNonce": 41192816, 445 | "isDeleted": false, 446 | "id": "qixf0Ic54Vet_rYJR4Svr", 447 | "fillStyle": "solid", 448 | "strokeWidth": 2, 449 | "strokeStyle": "solid", 450 | "roughness": 0, 451 | "opacity": 100, 452 | "angle": 0, 453 | "x": 1230.8750498617424, 454 | "y": 486.1594841073545, 455 | "strokeColor": "#495057", 456 | "backgroundColor": "#ced4da", 457 | "width": 0.11921154948946984, 458 | "height": 21.898244629272952, 459 | "seed": 721661808, 460 | "groupIds": [ 461 | "TmclqM7Qu4H0ux2YfQlc-" 462 | ], 463 | "roundness": null, 464 | "boundElements": [], 465 | "updated": 1682873021995, 466 | "link": null, 467 | "locked": false, 468 | "startBinding": null, 469 | "endBinding": null, 470 | "lastCommittedPoint": null, 471 | "startArrowhead": null, 472 | "endArrowhead": null, 473 | "points": [ 474 | [ 475 | 0, 476 | 0 477 | ], 478 | [ 479 | 0.11921154948946984, 480 | 21.898244629272952 481 | ] 482 | ] 483 | }, 484 | { 485 | "type": "line", 486 | "version": 751, 487 | "versionNonce": 1996046736, 488 | "isDeleted": false, 489 | "id": "m8C5dAYZfh8Ys5oIsNfj0", 490 | "fillStyle": "solid", 491 | "strokeWidth": 2, 492 | "strokeStyle": "solid", 493 | "roughness": 0, 494 | "opacity": 100, 495 | "angle": 0, 496 | "x": 1274.1580124455681, 497 | "y": 511.5882246253524, 498 | "strokeColor": "#495057", 499 | "backgroundColor": "#ced4da", 500 | "width": 0.11921154948946984, 501 | "height": 21.898244629272952, 502 | "seed": 1337737584, 503 | "groupIds": [ 504 | "TmclqM7Qu4H0ux2YfQlc-" 505 | ], 506 | "roundness": null, 507 | "boundElements": [], 508 | "updated": 1682873021995, 509 | "link": null, 510 | "locked": false, 511 | "startBinding": null, 512 | "endBinding": null, 513 | "lastCommittedPoint": null, 514 | "startArrowhead": null, 515 | "endArrowhead": null, 516 | "points": [ 517 | [ 518 | 0, 519 | 0 520 | ], 521 | [ 522 | 0.11921154948946984, 523 | 21.898244629272952 524 | ] 525 | ] 526 | }, 527 | { 528 | "type": "line", 529 | "version": 1107, 530 | "versionNonce": 23158640, 531 | "isDeleted": false, 532 | "id": "hqNipTREaG9QCqQ0FYi_h", 533 | "fillStyle": "solid", 534 | "strokeWidth": 2, 535 | "strokeStyle": "solid", 536 | "roughness": 0, 537 | "opacity": 100, 538 | "angle": 0, 539 | "x": 1213.5072848672714, 540 | "y": 552.6956656510354, 541 | "strokeColor": "#495057", 542 | "backgroundColor": "#ced4da", 543 | "width": 131.15255459021012, 544 | "height": 57.286745112229376, 545 | "seed": 865244016, 546 | "groupIds": [ 547 | "TmclqM7Qu4H0ux2YfQlc-" 548 | ], 549 | "roundness": null, 550 | "boundElements": [], 551 | "updated": 1682873021995, 552 | "link": null, 553 | "locked": false, 554 | "startBinding": null, 555 | "endBinding": null, 556 | "lastCommittedPoint": null, 557 | "startArrowhead": null, 558 | "endArrowhead": null, 559 | "points": [ 560 | [ 561 | 0, 562 | 0 563 | ], 564 | [ 565 | 97.40179349378212, 566 | 57.286745112229376 567 | ], 568 | [ 569 | 131.15255459021012, 570 | 35.823512454989384 571 | ] 572 | ] 573 | }, 574 | { 575 | "type": "line", 576 | "version": 863, 577 | "versionNonce": 1199214480, 578 | "isDeleted": false, 579 | "id": "BDSVay8SJNJeIbmIOvHTx", 580 | "fillStyle": "solid", 581 | "strokeWidth": 2, 582 | "strokeStyle": "solid", 583 | "roughness": 0, 584 | "opacity": 100, 585 | "angle": 0, 586 | "x": 1284.9787530915228, 587 | "y": 543.5094095309229, 588 | "strokeColor": "#495057", 589 | "backgroundColor": "#ced4da", 590 | "width": 0.11921154948946984, 591 | "height": 21.898244629272952, 592 | "seed": 751740272, 593 | "groupIds": [ 594 | "TmclqM7Qu4H0ux2YfQlc-" 595 | ], 596 | "roundness": null, 597 | "boundElements": [], 598 | "updated": 1682873021995, 599 | "link": null, 600 | "locked": false, 601 | "startBinding": null, 602 | "endBinding": null, 603 | "lastCommittedPoint": null, 604 | "startArrowhead": null, 605 | "endArrowhead": null, 606 | "points": [ 607 | [ 608 | 0, 609 | 0 610 | ], 611 | [ 612 | 0.11921154948946984, 613 | 21.898244629272952 614 | ] 615 | ] 616 | }, 617 | { 618 | "type": "line", 619 | "version": 888, 620 | "versionNonce": 1860432240, 621 | "isDeleted": false, 622 | "id": "Udu77oLbw-ARQreW-4Cm1", 623 | "fillStyle": "solid", 624 | "strokeWidth": 2, 625 | "strokeStyle": "solid", 626 | "roughness": 0, 627 | "opacity": 100, 628 | "angle": 0, 629 | "x": 1242.7778645722956, 630 | "y": 521.8679282390112, 631 | "strokeColor": "#495057", 632 | "backgroundColor": "#ced4da", 633 | "width": 0.11921154948946984, 634 | "height": 19.19305946778427, 635 | "seed": 146306928, 636 | "groupIds": [ 637 | "TmclqM7Qu4H0ux2YfQlc-" 638 | ], 639 | "roundness": null, 640 | "boundElements": [], 641 | "updated": 1682873021995, 642 | "link": null, 643 | "locked": false, 644 | "startBinding": null, 645 | "endBinding": null, 646 | "lastCommittedPoint": null, 647 | "startArrowhead": null, 648 | "endArrowhead": null, 649 | "points": [ 650 | [ 651 | 0, 652 | 0 653 | ], 654 | [ 655 | 0.11921154948946984, 656 | 19.19305946778427 657 | ] 658 | ] 659 | }, 660 | { 661 | "type": "line", 662 | "version": 903, 663 | "versionNonce": 1453464976, 664 | "isDeleted": false, 665 | "id": "QhVrr8KkixlKyYXTvJOpj", 666 | "fillStyle": "solid", 667 | "strokeWidth": 2, 668 | "strokeStyle": "solid", 669 | "roughness": 0, 670 | "opacity": 100, 671 | "angle": 0, 672 | "x": 1270.9117902517812, 673 | "y": 559.7405204998561, 674 | "strokeColor": "#495057", 675 | "backgroundColor": "#ced4da", 676 | "width": 0.11921154948946984, 677 | "height": 21.898244629272952, 678 | "seed": 731384176, 679 | "groupIds": [ 680 | "TmclqM7Qu4H0ux2YfQlc-" 681 | ], 682 | "roundness": null, 683 | "boundElements": [], 684 | "updated": 1682873021995, 685 | "link": null, 686 | "locked": false, 687 | "startBinding": null, 688 | "endBinding": null, 689 | "lastCommittedPoint": null, 690 | "startArrowhead": null, 691 | "endArrowhead": null, 692 | "points": [ 693 | [ 694 | 0, 695 | 0 696 | ], 697 | [ 698 | 0.11921154948946984, 699 | 21.898244629272952 700 | ] 701 | ] 702 | }, 703 | { 704 | "type": "line", 705 | "version": 992, 706 | "versionNonce": 1917076336, 707 | "isDeleted": false, 708 | "id": "FWkvcemgmuCAIJtezOEFU", 709 | "fillStyle": "solid", 710 | "strokeWidth": 2, 711 | "strokeStyle": "solid", 712 | "roughness": 0, 713 | "opacity": 100, 714 | "angle": 0, 715 | "x": 1228.1698647002559, 716 | "y": 537.0169651433503, 717 | "strokeColor": "#495057", 718 | "backgroundColor": "#ced4da", 719 | "width": 0.11921154948946984, 720 | "height": 21.898244629272952, 721 | "seed": 817191792, 722 | "groupIds": [ 723 | "TmclqM7Qu4H0ux2YfQlc-" 724 | ], 725 | "roundness": null, 726 | "boundElements": [], 727 | "updated": 1682873021995, 728 | "link": null, 729 | "locked": false, 730 | "startBinding": null, 731 | "endBinding": null, 732 | "lastCommittedPoint": null, 733 | "startArrowhead": null, 734 | "endArrowhead": null, 735 | "points": [ 736 | [ 737 | 0, 738 | 0 739 | ], 740 | [ 741 | 0.11921154948946984, 742 | 21.898244629272952 743 | ] 744 | ] 745 | }, 746 | { 747 | "type": "line", 748 | "version": 941, 749 | "versionNonce": 1973939088, 750 | "isDeleted": false, 751 | "id": "Ji7lFB5Jv_BNi1hSS6QND", 752 | "fillStyle": "solid", 753 | "strokeWidth": 2, 754 | "strokeStyle": "solid", 755 | "roughness": 0, 756 | "opacity": 100, 757 | "angle": 0, 758 | "x": 1243.8599386368921, 759 | "y": 572.7254092750036, 760 | "strokeColor": "#495057", 761 | "backgroundColor": "#ced4da", 762 | "width": 0.11921154948946984, 763 | "height": 21.898244629272952, 764 | "seed": 173567344, 765 | "groupIds": [ 766 | "TmclqM7Qu4H0ux2YfQlc-" 767 | ], 768 | "roundness": null, 769 | "boundElements": [], 770 | "updated": 1682873021995, 771 | "link": null, 772 | "locked": false, 773 | "startBinding": null, 774 | "endBinding": null, 775 | "lastCommittedPoint": null, 776 | "startArrowhead": null, 777 | "endArrowhead": null, 778 | "points": [ 779 | [ 780 | 0, 781 | 0 782 | ], 783 | [ 784 | 0.11921154948946984, 785 | 21.898244629272952 786 | ] 787 | ] 788 | }, 789 | { 790 | "type": "line", 791 | "version": 981, 792 | "versionNonce": 164255088, 793 | "isDeleted": false, 794 | "id": "5z9ZJR29Y6S612G1frKY5", 795 | "fillStyle": "solid", 796 | "strokeWidth": 2, 797 | "strokeStyle": "solid", 798 | "roughness": 0, 799 | "opacity": 100, 800 | "angle": 0, 801 | "x": 1282.273567930034, 802 | "y": 593.8258535346172, 803 | "strokeColor": "#495057", 804 | "backgroundColor": "#ced4da", 805 | "width": 0.11921154948946984, 806 | "height": 20.27513353238065, 807 | "seed": 566837104, 808 | "groupIds": [ 809 | "TmclqM7Qu4H0ux2YfQlc-" 810 | ], 811 | "roundness": null, 812 | "boundElements": [], 813 | "updated": 1682873021995, 814 | "link": null, 815 | "locked": false, 816 | "startBinding": null, 817 | "endBinding": null, 818 | "lastCommittedPoint": null, 819 | "startArrowhead": null, 820 | "endArrowhead": null, 821 | "points": [ 822 | [ 823 | 0, 824 | 0 825 | ], 826 | [ 827 | 0.11921154948946984, 828 | 20.27513353238065 829 | ] 830 | ] 831 | }, 832 | { 833 | "type": "line", 834 | "version": 1018, 835 | "versionNonce": 247724432, 836 | "isDeleted": false, 837 | "id": "mNtpgRFnmZTtK1QW4pLAz", 838 | "fillStyle": "solid", 839 | "strokeWidth": 2, 840 | "strokeStyle": "solid", 841 | "roughness": 0, 842 | "opacity": 100, 843 | "angle": 0, 844 | "x": 1269.8297161871847, 845 | "y": 609.5159274712544, 846 | "strokeColor": "#495057", 847 | "backgroundColor": "#ced4da", 848 | "width": 0.11921154948946984, 849 | "height": 19.19305946778427, 850 | "seed": 2001719664, 851 | "groupIds": [ 852 | "TmclqM7Qu4H0ux2YfQlc-" 853 | ], 854 | "roundness": null, 855 | "boundElements": [], 856 | "updated": 1682873021995, 857 | "link": null, 858 | "locked": false, 859 | "startBinding": null, 860 | "endBinding": null, 861 | "lastCommittedPoint": null, 862 | "startArrowhead": null, 863 | "endArrowhead": null, 864 | "points": [ 865 | [ 866 | 0, 867 | 0 868 | ], 869 | [ 870 | 0.11921154948946984, 871 | 19.19305946778427 872 | ] 873 | ] 874 | }, 875 | { 876 | "type": "line", 877 | "version": 1047, 878 | "versionNonce": 717105008, 879 | "isDeleted": false, 880 | "id": "SKftVYbFKL-tBtBhulmTS", 881 | "fillStyle": "solid", 882 | "strokeWidth": 2, 883 | "strokeStyle": "solid", 884 | "roughness": 0, 885 | "opacity": 100, 886 | "angle": 0, 887 | "x": 1229.7929757971483, 888 | "y": 588.4154832116409, 889 | "strokeColor": "#495057", 890 | "backgroundColor": "#ced4da", 891 | "width": 0.11921154948946984, 892 | "height": 19.19305946778427, 893 | "seed": 1110713200, 894 | "groupIds": [ 895 | "TmclqM7Qu4H0ux2YfQlc-" 896 | ], 897 | "roundness": null, 898 | "boundElements": [], 899 | "updated": 1682873021995, 900 | "link": null, 901 | "locked": false, 902 | "startBinding": null, 903 | "endBinding": null, 904 | "lastCommittedPoint": null, 905 | "startArrowhead": null, 906 | "endArrowhead": null, 907 | "points": [ 908 | [ 909 | 0, 910 | 0 911 | ], 912 | [ 913 | 0.11921154948946984, 914 | 19.19305946778427 915 | ] 916 | ] 917 | }, 918 | { 919 | "type": "ellipse", 920 | "version": 1131, 921 | "versionNonce": 1018185967, 922 | "isDeleted": false, 923 | "id": "Yqz9xnM2tAhcIeS9ozvdr", 924 | "fillStyle": "hachure", 925 | "strokeWidth": 1, 926 | "strokeStyle": "solid", 927 | "roughness": 1, 928 | "opacity": 40, 929 | "angle": 0, 930 | "x": 701, 931 | "y": 455, 932 | "strokeColor": "#000000", 933 | "backgroundColor": "transparent", 934 | "width": 345.9999999999999, 935 | "height": 284, 936 | "seed": 2108607856, 937 | "groupIds": [], 938 | "roundness": { 939 | "type": 2 940 | }, 941 | "boundElements": [ 942 | { 943 | "id": "IFLo1dVYjrxbjleYs7-B1", 944 | "type": "arrow" 945 | } 946 | ], 947 | "updated": 1682936347161, 948 | "link": null, 949 | "locked": false 950 | }, 951 | { 952 | "type": "rectangle", 953 | "version": 1711, 954 | "versionNonce": 278007169, 955 | "isDeleted": false, 956 | "id": "BPGgjUzZW_m4V3-yse6UM", 957 | "fillStyle": "cross-hatch", 958 | "strokeWidth": 2, 959 | "strokeStyle": "solid", 960 | "roughness": 0, 961 | "opacity": 100, 962 | "angle": 0, 963 | "x": 814.6198836620213, 964 | "y": 480.77024667022, 965 | "strokeColor": "#000000", 966 | "backgroundColor": "#ced4da", 967 | "width": 72.76464829190567, 968 | "height": 135.82734347822353, 969 | "seed": 613182864, 970 | "groupIds": [ 971 | "WcNMD_7wJ-LARkroIjJ1m" 972 | ], 973 | "roundness": null, 974 | "boundElements": [ 975 | { 976 | "id": "h2fiyrVjBZW9hFVpdYGPZ", 977 | "type": "arrow" 978 | } 979 | ], 980 | "updated": 1682936328722, 981 | "link": null, 982 | "locked": false 983 | }, 984 | { 985 | "type": "text", 986 | "version": 2256, 987 | "versionNonce": 2099416367, 988 | "isDeleted": false, 989 | "id": "fStyPMfWNDI1Ym2PxNWCt", 990 | "fillStyle": "hachure", 991 | "strokeWidth": 1, 992 | "strokeStyle": "solid", 993 | "roughness": 1, 994 | "opacity": 100, 995 | "angle": 0, 996 | "x": 789.5, 997 | "y": 635.9110432065776, 998 | "strokeColor": "#000000", 999 | "backgroundColor": "transparent", 1000 | "width": 120.46666717529297, 1001 | "height": 48.81127603807368, 1002 | "seed": 835206032, 1003 | "groupIds": [ 1004 | "WcNMD_7wJ-LARkroIjJ1m" 1005 | ], 1006 | "roundness": null, 1007 | "boundElements": [], 1008 | "updated": 1682936328723, 1009 | "link": null, 1010 | "locked": false, 1011 | "fontSize": 39.04902083045894, 1012 | "fontFamily": 1, 1013 | "text": "Server", 1014 | "textAlign": "left", 1015 | "verticalAlign": "top", 1016 | "containerId": null, 1017 | "originalText": "Server", 1018 | "lineHeight": 1.25, 1019 | "baseline": 34 1020 | }, 1021 | { 1022 | "type": "line", 1023 | "version": 1553, 1024 | "versionNonce": 1174102337, 1025 | "isDeleted": false, 1026 | "id": "l-8QFyBVN4r0oRdFwHA4V", 1027 | "fillStyle": "cross-hatch", 1028 | "strokeWidth": 2, 1029 | "strokeStyle": "solid", 1030 | "roughness": 0, 1031 | "opacity": 100, 1032 | "angle": 0, 1033 | "x": 823.375762297861, 1034 | "y": 495.79621356347434, 1035 | "strokeColor": "#000000", 1036 | "backgroundColor": "#ced4da", 1037 | "width": 55.78623035712741, 1038 | "height": 4.43935969434039e-13, 1039 | "seed": 39471504, 1040 | "groupIds": [ 1041 | "WcNMD_7wJ-LARkroIjJ1m" 1042 | ], 1043 | "roundness": { 1044 | "type": 2 1045 | }, 1046 | "boundElements": [], 1047 | "updated": 1682936328723, 1048 | "link": null, 1049 | "locked": false, 1050 | "startBinding": null, 1051 | "endBinding": null, 1052 | "lastCommittedPoint": null, 1053 | "startArrowhead": null, 1054 | "endArrowhead": null, 1055 | "points": [ 1056 | [ 1057 | 0, 1058 | 0 1059 | ], 1060 | [ 1061 | 55.78623035712741, 1062 | 4.43935969434039e-13 1063 | ] 1064 | ] 1065 | }, 1066 | { 1067 | "type": "line", 1068 | "version": 1585, 1069 | "versionNonce": 852954959, 1070 | "isDeleted": false, 1071 | "id": "_FPrLJdi9xmInv2zzcwXU", 1072 | "fillStyle": "cross-hatch", 1073 | "strokeWidth": 2, 1074 | "strokeStyle": "solid", 1075 | "roughness": 0, 1076 | "opacity": 100, 1077 | "angle": 0, 1078 | "x": 823.1090926294091, 1079 | "y": 508.2993152040683, 1080 | "strokeColor": "#000000", 1081 | "backgroundColor": "#ced4da", 1082 | "width": 55.78623035712741, 1083 | "height": 4.43935969434039e-13, 1084 | "seed": 1366988688, 1085 | "groupIds": [ 1086 | "WcNMD_7wJ-LARkroIjJ1m" 1087 | ], 1088 | "roundness": { 1089 | "type": 2 1090 | }, 1091 | "boundElements": [], 1092 | "updated": 1682936328723, 1093 | "link": null, 1094 | "locked": false, 1095 | "startBinding": null, 1096 | "endBinding": null, 1097 | "lastCommittedPoint": null, 1098 | "startArrowhead": null, 1099 | "endArrowhead": null, 1100 | "points": [ 1101 | [ 1102 | 0, 1103 | 0 1104 | ], 1105 | [ 1106 | 55.78623035712741, 1107 | 4.43935969434039e-13 1108 | ] 1109 | ] 1110 | }, 1111 | { 1112 | "type": "line", 1113 | "version": 1567, 1114 | "versionNonce": 1432326433, 1115 | "isDeleted": false, 1116 | "id": "f-TDODURoJbLMtcjF3rxG", 1117 | "fillStyle": "cross-hatch", 1118 | "strokeWidth": 2, 1119 | "strokeStyle": "solid", 1120 | "roughness": 0, 1121 | "opacity": 100, 1122 | "angle": 0, 1123 | "x": 823.0487905273822, 1124 | "y": 520.8024168446638, 1125 | "strokeColor": "#000000", 1126 | "backgroundColor": "#ced4da", 1127 | "width": 55.78623035712741, 1128 | "height": 4.43935969434039e-13, 1129 | "seed": 862004624, 1130 | "groupIds": [ 1131 | "WcNMD_7wJ-LARkroIjJ1m" 1132 | ], 1133 | "roundness": { 1134 | "type": 2 1135 | }, 1136 | "boundElements": [], 1137 | "updated": 1682936328723, 1138 | "link": null, 1139 | "locked": false, 1140 | "startBinding": null, 1141 | "endBinding": null, 1142 | "lastCommittedPoint": null, 1143 | "startArrowhead": null, 1144 | "endArrowhead": null, 1145 | "points": [ 1146 | [ 1147 | 0, 1148 | 0 1149 | ], 1150 | [ 1151 | 55.78623035712741, 1152 | 4.43935969434039e-13 1153 | ] 1154 | ] 1155 | }, 1156 | { 1157 | "type": "line", 1158 | "version": 1571, 1159 | "versionNonce": 1593199983, 1160 | "isDeleted": false, 1161 | "id": "__DkKUI-tBbYHO5-nK9Cp", 1162 | "fillStyle": "cross-hatch", 1163 | "strokeWidth": 2, 1164 | "strokeStyle": "solid", 1165 | "roughness": 0, 1166 | "opacity": 100, 1167 | "angle": 0, 1168 | "x": 823.0487905273822, 1169 | "y": 533.3055184852585, 1170 | "strokeColor": "#000000", 1171 | "backgroundColor": "#ced4da", 1172 | "width": 55.78623035712741, 1173 | "height": 4.43935969434039e-13, 1174 | "seed": 1967518608, 1175 | "groupIds": [ 1176 | "WcNMD_7wJ-LARkroIjJ1m" 1177 | ], 1178 | "roundness": { 1179 | "type": 2 1180 | }, 1181 | "boundElements": [], 1182 | "updated": 1682936328723, 1183 | "link": null, 1184 | "locked": false, 1185 | "startBinding": null, 1186 | "endBinding": null, 1187 | "lastCommittedPoint": null, 1188 | "startArrowhead": null, 1189 | "endArrowhead": null, 1190 | "points": [ 1191 | [ 1192 | 0, 1193 | 0 1194 | ], 1195 | [ 1196 | 55.78623035712741, 1197 | 4.43935969434039e-13 1198 | ] 1199 | ] 1200 | }, 1201 | { 1202 | "type": "rectangle", 1203 | "version": 4033, 1204 | "versionNonce": 1624581921, 1205 | "isDeleted": false, 1206 | "id": "KYvha5HHwrWdtWGw_QI1R", 1207 | "fillStyle": "cross-hatch", 1208 | "strokeWidth": 2, 1209 | "strokeStyle": "solid", 1210 | "roughness": 0, 1211 | "opacity": 100, 1212 | "angle": 0, 1213 | "x": 823.9223355882809, 1214 | "y": 541.2383908334516, 1215 | "strokeColor": "#000000", 1216 | "backgroundColor": "#ced4da", 1217 | "width": 52.92374702084933, 1218 | "height": 52.873067543499936, 1219 | "seed": 1847242640, 1220 | "groupIds": [ 1221 | "vHUrcTftq6IWB6q_Gfptn", 1222 | "PSfvryV62YZeweI74LDTY", 1223 | "O9mVIfPwiJ5xmfgI98H2X", 1224 | "8z7ofuPCeP9klvtKrFLHh", 1225 | "vfjgvYsW5r24Ax0x1j1T3", 1226 | "JCMUISx25F40zadhSXtF7" 1227 | ], 1228 | "roundness": null, 1229 | "boundElements": [ 1230 | { 1231 | "id": "h2fiyrVjBZW9hFVpdYGPZ", 1232 | "type": "arrow" 1233 | } 1234 | ], 1235 | "updated": 1682936334638, 1236 | "link": null, 1237 | "locked": false 1238 | }, 1239 | { 1240 | "type": "line", 1241 | "version": 3266, 1242 | "versionNonce": 533534575, 1243 | "isDeleted": false, 1244 | "id": "-oloeVVhFkkZ0ZCEeoqkR", 1245 | "fillStyle": "solid", 1246 | "strokeWidth": 2, 1247 | "strokeStyle": "solid", 1248 | "roughness": 0, 1249 | "opacity": 100, 1250 | "angle": 0, 1251 | "x": 836.3545666104044, 1252 | "y": 570.2646067885297, 1253 | "strokeColor": "#0091e2", 1254 | "backgroundColor": "#0091e2", 1255 | "width": 46.48713442734936, 1256 | "height": 22.809000920556798, 1257 | "seed": 1950236048, 1258 | "groupIds": [ 1259 | "UQS5v2mr0DCHql7j6XTMk", 1260 | "vfjgvYsW5r24Ax0x1j1T3", 1261 | "JCMUISx25F40zadhSXtF7" 1262 | ], 1263 | "roundness": { 1264 | "type": 2 1265 | }, 1266 | "boundElements": [], 1267 | "updated": 1682936334638, 1268 | "link": null, 1269 | "locked": false, 1270 | "startBinding": null, 1271 | "endBinding": null, 1272 | "lastCommittedPoint": null, 1273 | "startArrowhead": null, 1274 | "endArrowhead": null, 1275 | "points": [ 1276 | [ 1277 | 0, 1278 | 0 1279 | ], 1280 | [ 1281 | 21.75638446138932, 1282 | -0.4561081556793558 1283 | ], 1284 | [ 1285 | 26.986006374058764, 1286 | -1.1098081517203058 1287 | ], 1288 | [ 1289 | 25.90144967612373, 1290 | -5.314299391984548 1291 | ], 1292 | [ 1293 | 27.62484905327285, 1294 | -9.310795178962897 1295 | ], 1296 | [ 1297 | 30.284223481592495, 1298 | -5.581724860680294 1299 | ], 1300 | [ 1301 | 29.92766732841389, 1302 | -2.5806386288978684 1303 | ], 1304 | [ 1305 | 34.213874559328495, 1306 | -5.514869179267024 1307 | ], 1308 | [ 1309 | 37.56409875931184, 1310 | -4.2891801904850935 1311 | ], 1312 | [ 1313 | 35.57327067168915, 1314 | -1.5926602257905886 1315 | ], 1316 | [ 1317 | 31.190491380134898, 1318 | 0.47987687019200165 1319 | ], 1320 | [ 1321 | 19.75072075191394, 1322 | 12.253936877650219 1323 | ], 1324 | [ 1325 | 0.1396620199504468, 1326 | 13.498205741593903 1327 | ], 1328 | [ 1329 | -8.923035668037521, 1330 | 0.9151799944572949 1331 | ], 1332 | [ 1333 | -2.9416609543552075, 1334 | -0.4902749970307942 1335 | ], 1336 | [ 1337 | -0.23579100438095144, 1338 | -0.016047053939279094 1339 | ] 1340 | ] 1341 | }, 1342 | { 1343 | "type": "rectangle", 1344 | "version": 2171, 1345 | "versionNonce": 1623563009, 1346 | "isDeleted": false, 1347 | "id": "asMpCOSIIEeu8Ns7neVl_", 1348 | "fillStyle": "solid", 1349 | "strokeWidth": 2, 1350 | "strokeStyle": "solid", 1351 | "roughness": 0, 1352 | "opacity": 100, 1353 | "angle": 0, 1354 | "x": 831.194504287819, 1355 | "y": 563.8506211402982, 1356 | "strokeColor": "#0091e2", 1357 | "backgroundColor": "#0091e2", 1358 | "width": 4.5700739194192055, 1359 | "height": 4.5700739194192055, 1360 | "seed": 1254099856, 1361 | "groupIds": [ 1362 | "UQS5v2mr0DCHql7j6XTMk", 1363 | "vfjgvYsW5r24Ax0x1j1T3", 1364 | "JCMUISx25F40zadhSXtF7" 1365 | ], 1366 | "roundness": null, 1367 | "boundElements": [], 1368 | "updated": 1682936334638, 1369 | "link": null, 1370 | "locked": false 1371 | }, 1372 | { 1373 | "type": "rectangle", 1374 | "version": 2226, 1375 | "versionNonce": 741757327, 1376 | "isDeleted": false, 1377 | "id": "2-e7p6E9BQweZuP0h0u4f", 1378 | "fillStyle": "solid", 1379 | "strokeWidth": 2, 1380 | "strokeStyle": "solid", 1381 | "roughness": 0, 1382 | "opacity": 100, 1383 | "angle": 0, 1384 | "x": 836.9070950890273, 1385 | "y": 563.641162854831, 1386 | "strokeColor": "#0091e2", 1387 | "backgroundColor": "#0091e2", 1388 | "width": 4.5700739194192055, 1389 | "height": 4.5700739194192055, 1390 | "seed": 1272453520, 1391 | "groupIds": [ 1392 | "UQS5v2mr0DCHql7j6XTMk", 1393 | "vfjgvYsW5r24Ax0x1j1T3", 1394 | "JCMUISx25F40zadhSXtF7" 1395 | ], 1396 | "roundness": null, 1397 | "boundElements": [], 1398 | "updated": 1682936334638, 1399 | "link": null, 1400 | "locked": false 1401 | }, 1402 | { 1403 | "type": "rectangle", 1404 | "version": 2246, 1405 | "versionNonce": 373068513, 1406 | "isDeleted": false, 1407 | "id": "H66IH_xqdZBwLvSxvfDrk", 1408 | "fillStyle": "solid", 1409 | "strokeWidth": 2, 1410 | "strokeStyle": "solid", 1411 | "roughness": 0, 1412 | "opacity": 100, 1413 | "angle": 0, 1414 | "x": 842.8862685815366, 1415 | "y": 563.7363627402481, 1416 | "strokeColor": "#0091e2", 1417 | "backgroundColor": "#0091e2", 1418 | "width": 4.5700739194192055, 1419 | "height": 4.5700739194192055, 1420 | "seed": 1253992336, 1421 | "groupIds": [ 1422 | "UQS5v2mr0DCHql7j6XTMk", 1423 | "vfjgvYsW5r24Ax0x1j1T3", 1424 | "JCMUISx25F40zadhSXtF7" 1425 | ], 1426 | "roundness": null, 1427 | "boundElements": [], 1428 | "updated": 1682936334638, 1429 | "link": null, 1430 | "locked": false 1431 | }, 1432 | { 1433 | "type": "rectangle", 1434 | "version": 2267, 1435 | "versionNonce": 1739802543, 1436 | "isDeleted": false, 1437 | "id": "ikjs4PgV3_AD3PyGf_nph", 1438 | "fillStyle": "solid", 1439 | "strokeWidth": 2, 1440 | "strokeStyle": "solid", 1441 | "roughness": 0, 1442 | "opacity": 100, 1443 | "angle": 0, 1444 | "x": 848.8463995400616, 1445 | "y": 563.4507439072205, 1446 | "strokeColor": "#0091e2", 1447 | "backgroundColor": "#0091e2", 1448 | "width": 4.5700739194192055, 1449 | "height": 4.5700739194192055, 1450 | "seed": 1940583824, 1451 | "groupIds": [ 1452 | "UQS5v2mr0DCHql7j6XTMk", 1453 | "vfjgvYsW5r24Ax0x1j1T3", 1454 | "JCMUISx25F40zadhSXtF7" 1455 | ], 1456 | "roundness": null, 1457 | "boundElements": [], 1458 | "updated": 1682936334638, 1459 | "link": null, 1460 | "locked": false 1461 | }, 1462 | { 1463 | "type": "rectangle", 1464 | "version": 2309, 1465 | "versionNonce": 365530817, 1466 | "isDeleted": false, 1467 | "id": "jW3qlBxa8Q71d1l5nkz-M", 1468 | "fillStyle": "solid", 1469 | "strokeWidth": 2, 1470 | "strokeStyle": "solid", 1471 | "roughness": 0, 1472 | "opacity": 100, 1473 | "angle": 0, 1474 | "x": 854.5018595431823, 1475 | "y": 563.526907650907, 1476 | "strokeColor": "#0091e2", 1477 | "backgroundColor": "#0091e2", 1478 | "width": 4.5700739194192055, 1479 | "height": 4.5700739194192055, 1480 | "seed": 735429520, 1481 | "groupIds": [ 1482 | "UQS5v2mr0DCHql7j6XTMk", 1483 | "vfjgvYsW5r24Ax0x1j1T3", 1484 | "JCMUISx25F40zadhSXtF7" 1485 | ], 1486 | "roundness": null, 1487 | "boundElements": [], 1488 | "updated": 1682936334638, 1489 | "link": null, 1490 | "locked": false 1491 | }, 1492 | { 1493 | "type": "rectangle", 1494 | "version": 2213, 1495 | "versionNonce": 812415439, 1496 | "isDeleted": false, 1497 | "id": "7zqsGEnX9RGSBCZ2nciPz", 1498 | "fillStyle": "solid", 1499 | "strokeWidth": 2, 1500 | "strokeStyle": "solid", 1501 | "roughness": 0, 1502 | "opacity": 100, 1503 | "angle": 0, 1504 | "x": 836.7452319520755, 1505 | "y": 558.0999580556305, 1506 | "strokeColor": "#0091e2", 1507 | "backgroundColor": "#0091e2", 1508 | "width": 4.5700739194192055, 1509 | "height": 4.5700739194192055, 1510 | "seed": 1903612304, 1511 | "groupIds": [ 1512 | "UQS5v2mr0DCHql7j6XTMk", 1513 | "vfjgvYsW5r24Ax0x1j1T3", 1514 | "JCMUISx25F40zadhSXtF7" 1515 | ], 1516 | "roundness": null, 1517 | "boundElements": [], 1518 | "updated": 1682936334638, 1519 | "link": null, 1520 | "locked": false 1521 | }, 1522 | { 1523 | "type": "rectangle", 1524 | "version": 2237, 1525 | "versionNonce": 906759841, 1526 | "isDeleted": false, 1527 | "id": "Mvk6mNvc5evXcqWxmbze9", 1528 | "fillStyle": "solid", 1529 | "strokeWidth": 2, 1530 | "strokeStyle": "solid", 1531 | "roughness": 0, 1532 | "opacity": 100, 1533 | "angle": 0, 1534 | "x": 842.7434511747015, 1535 | "y": 558.1761249954452, 1536 | "strokeColor": "#0091e2", 1537 | "backgroundColor": "#0091e2", 1538 | "width": 4.5700739194192055, 1539 | "height": 4.5700739194192055, 1540 | "seed": 1002839952, 1541 | "groupIds": [ 1542 | "UQS5v2mr0DCHql7j6XTMk", 1543 | "vfjgvYsW5r24Ax0x1j1T3", 1544 | "JCMUISx25F40zadhSXtF7" 1545 | ], 1546 | "roundness": null, 1547 | "boundElements": [], 1548 | "updated": 1682936334638, 1549 | "link": null, 1550 | "locked": false 1551 | }, 1552 | { 1553 | "type": "rectangle", 1554 | "version": 2261, 1555 | "versionNonce": 192571375, 1556 | "isDeleted": false, 1557 | "id": "2xQXECzbhS9sJqMTv019h", 1558 | "fillStyle": "solid", 1559 | "strokeWidth": 2, 1560 | "strokeStyle": "solid", 1561 | "roughness": 0, 1562 | "opacity": 100, 1563 | "angle": 0, 1564 | "x": 848.8178373371447, 1565 | "y": 558.176118603192, 1566 | "strokeColor": "#0091e2", 1567 | "backgroundColor": "#0091e2", 1568 | "width": 4.5700739194192055, 1569 | "height": 4.5700739194192055, 1570 | "seed": 1526443408, 1571 | "groupIds": [ 1572 | "UQS5v2mr0DCHql7j6XTMk", 1573 | "vfjgvYsW5r24Ax0x1j1T3", 1574 | "JCMUISx25F40zadhSXtF7" 1575 | ], 1576 | "roundness": null, 1577 | "boundElements": [], 1578 | "updated": 1682936334638, 1579 | "link": null, 1580 | "locked": false 1581 | }, 1582 | { 1583 | "type": "rectangle", 1584 | "version": 2280, 1585 | "versionNonce": 1303614081, 1586 | "isDeleted": false, 1587 | "id": "XbbgTuXkGxSc_PTG1xf4D", 1588 | "fillStyle": "solid", 1589 | "strokeWidth": 2, 1590 | "strokeStyle": "solid", 1591 | "roughness": 0, 1592 | "opacity": 100, 1593 | "angle": 0, 1594 | "x": 848.6178891322204, 1595 | "y": 552.3492789903203, 1596 | "strokeColor": "#0091e2", 1597 | "backgroundColor": "#0091e2", 1598 | "width": 4.5700739194192055, 1599 | "height": 4.5700739194192055, 1600 | "seed": 1481603984, 1601 | "groupIds": [ 1602 | "UQS5v2mr0DCHql7j6XTMk", 1603 | "vfjgvYsW5r24Ax0x1j1T3", 1604 | "JCMUISx25F40zadhSXtF7" 1605 | ], 1606 | "roundness": null, 1607 | "boundElements": [], 1608 | "updated": 1682936334638, 1609 | "link": null, 1610 | "locked": false 1611 | }, 1612 | { 1613 | "type": "text", 1614 | "version": 2792, 1615 | "versionNonce": 1189450255, 1616 | "isDeleted": false, 1617 | "id": "kRCwhzP-M02qEkAmMh4Jp", 1618 | "fillStyle": "hachure", 1619 | "strokeWidth": 1, 1620 | "strokeStyle": "solid", 1621 | "roughness": 1, 1622 | "opacity": 100, 1623 | "angle": 0, 1624 | "x": 822.8801161659476, 1625 | "y": 598.5325055792916, 1626 | "strokeColor": "#000000", 1627 | "backgroundColor": "transparent", 1628 | "width": 54.233333587646484, 1629 | "height": 20.525442487132864, 1630 | "seed": 1442417040, 1631 | "groupIds": [ 1632 | "JCMUISx25F40zadhSXtF7" 1633 | ], 1634 | "roundness": null, 1635 | "boundElements": [], 1636 | "updated": 1682936334638, 1637 | "link": null, 1638 | "locked": false, 1639 | "fontSize": 16.420353989706292, 1640 | "fontFamily": 1, 1641 | "text": "Docker", 1642 | "textAlign": "left", 1643 | "verticalAlign": "top", 1644 | "containerId": null, 1645 | "originalText": "Docker", 1646 | "lineHeight": 1.25, 1647 | "baseline": 15 1648 | }, 1649 | { 1650 | "type": "rectangle", 1651 | "version": 1630, 1652 | "versionNonce": 675615632, 1653 | "isDeleted": false, 1654 | "id": "VDqoMW0Zg79rD8Mjk9cue", 1655 | "fillStyle": "cross-hatch", 1656 | "strokeWidth": 2, 1657 | "strokeStyle": "solid", 1658 | "roughness": 0, 1659 | "opacity": 100, 1660 | "angle": 0, 1661 | "x": 1507.6176758540485, 1662 | "y": 478.02396371278417, 1663 | "strokeColor": "#000000", 1664 | "backgroundColor": "#ced4da", 1665 | "width": 72.76464829190567, 1666 | "height": 135.82734347822353, 1667 | "seed": 1402134384, 1668 | "groupIds": [ 1669 | "LlnIiHT0NvHZowiReLc0Y" 1670 | ], 1671 | "roundness": null, 1672 | "boundElements": [ 1673 | { 1674 | "id": "s6q4bBRN01Us9PnG6-d8_", 1675 | "type": "arrow" 1676 | }, 1677 | { 1678 | "id": "IFLo1dVYjrxbjleYs7-B1", 1679 | "type": "arrow" 1680 | } 1681 | ], 1682 | "updated": 1682873021995, 1683 | "link": null, 1684 | "locked": false 1685 | }, 1686 | { 1687 | "type": "text", 1688 | "version": 2174, 1689 | "versionNonce": 944200655, 1690 | "isDeleted": false, 1691 | "id": "izV-p-jIbQ98_BUxn6Zmz", 1692 | "fillStyle": "hachure", 1693 | "strokeWidth": 1, 1694 | "strokeStyle": "solid", 1695 | "roughness": 1, 1696 | "opacity": 100, 1697 | "angle": 0, 1698 | "x": 1482.4977921920272, 1699 | "y": 633.164760249142, 1700 | "strokeColor": "#000000", 1701 | "backgroundColor": "transparent", 1702 | "width": 120.46666717529297, 1703 | "height": 48.81127603807368, 1704 | "seed": 2128966000, 1705 | "groupIds": [ 1706 | "LlnIiHT0NvHZowiReLc0Y" 1707 | ], 1708 | "roundness": null, 1709 | "boundElements": [], 1710 | "updated": 1682936189434, 1711 | "link": null, 1712 | "locked": false, 1713 | "fontSize": 39.04902083045894, 1714 | "fontFamily": 1, 1715 | "text": "Server", 1716 | "textAlign": "left", 1717 | "verticalAlign": "top", 1718 | "containerId": null, 1719 | "originalText": "Server", 1720 | "lineHeight": 1.25, 1721 | "baseline": 34 1722 | }, 1723 | { 1724 | "type": "line", 1725 | "version": 1471, 1726 | "versionNonce": 1084321680, 1727 | "isDeleted": false, 1728 | "id": "-g6tsRqmHYEDS6tZcX32E", 1729 | "fillStyle": "cross-hatch", 1730 | "strokeWidth": 2, 1731 | "strokeStyle": "solid", 1732 | "roughness": 0, 1733 | "opacity": 100, 1734 | "angle": 0, 1735 | "x": 1516.3735544898882, 1736 | "y": 493.04993060603863, 1737 | "strokeColor": "#000000", 1738 | "backgroundColor": "#ced4da", 1739 | "width": 55.78623035712741, 1740 | "height": 4.43935969434039e-13, 1741 | "seed": 102089584, 1742 | "groupIds": [ 1743 | "LlnIiHT0NvHZowiReLc0Y" 1744 | ], 1745 | "roundness": { 1746 | "type": 2 1747 | }, 1748 | "boundElements": [], 1749 | "updated": 1682873021995, 1750 | "link": null, 1751 | "locked": false, 1752 | "startBinding": null, 1753 | "endBinding": null, 1754 | "lastCommittedPoint": null, 1755 | "startArrowhead": null, 1756 | "endArrowhead": null, 1757 | "points": [ 1758 | [ 1759 | 0, 1760 | 0 1761 | ], 1762 | [ 1763 | 55.78623035712741, 1764 | 4.43935969434039e-13 1765 | ] 1766 | ] 1767 | }, 1768 | { 1769 | "type": "line", 1770 | "version": 1503, 1771 | "versionNonce": 672862576, 1772 | "isDeleted": false, 1773 | "id": "qltNWGbhd5-2icZgz6qzY", 1774 | "fillStyle": "cross-hatch", 1775 | "strokeWidth": 2, 1776 | "strokeStyle": "solid", 1777 | "roughness": 0, 1778 | "opacity": 100, 1779 | "angle": 0, 1780 | "x": 1516.1068848214363, 1781 | "y": 505.5530322466326, 1782 | "strokeColor": "#000000", 1783 | "backgroundColor": "#ced4da", 1784 | "width": 55.78623035712741, 1785 | "height": 4.43935969434039e-13, 1786 | "seed": 661378416, 1787 | "groupIds": [ 1788 | "LlnIiHT0NvHZowiReLc0Y" 1789 | ], 1790 | "roundness": { 1791 | "type": 2 1792 | }, 1793 | "boundElements": [], 1794 | "updated": 1682873021995, 1795 | "link": null, 1796 | "locked": false, 1797 | "startBinding": null, 1798 | "endBinding": null, 1799 | "lastCommittedPoint": null, 1800 | "startArrowhead": null, 1801 | "endArrowhead": null, 1802 | "points": [ 1803 | [ 1804 | 0, 1805 | 0 1806 | ], 1807 | [ 1808 | 55.78623035712741, 1809 | 4.43935969434039e-13 1810 | ] 1811 | ] 1812 | }, 1813 | { 1814 | "type": "line", 1815 | "version": 1485, 1816 | "versionNonce": 1206713744, 1817 | "isDeleted": false, 1818 | "id": "uUsPpDqLtATPU9ew-KE64", 1819 | "fillStyle": "cross-hatch", 1820 | "strokeWidth": 2, 1821 | "strokeStyle": "solid", 1822 | "roughness": 0, 1823 | "opacity": 100, 1824 | "angle": 0, 1825 | "x": 1516.0465827194093, 1826 | "y": 518.0561338872279, 1827 | "strokeColor": "#000000", 1828 | "backgroundColor": "#ced4da", 1829 | "width": 55.78623035712741, 1830 | "height": 4.43935969434039e-13, 1831 | "seed": 405776240, 1832 | "groupIds": [ 1833 | "LlnIiHT0NvHZowiReLc0Y" 1834 | ], 1835 | "roundness": { 1836 | "type": 2 1837 | }, 1838 | "boundElements": [], 1839 | "updated": 1682873021995, 1840 | "link": null, 1841 | "locked": false, 1842 | "startBinding": null, 1843 | "endBinding": null, 1844 | "lastCommittedPoint": null, 1845 | "startArrowhead": null, 1846 | "endArrowhead": null, 1847 | "points": [ 1848 | [ 1849 | 0, 1850 | 0 1851 | ], 1852 | [ 1853 | 55.78623035712741, 1854 | 4.43935969434039e-13 1855 | ] 1856 | ] 1857 | }, 1858 | { 1859 | "type": "line", 1860 | "version": 1489, 1861 | "versionNonce": 932668272, 1862 | "isDeleted": false, 1863 | "id": "6whWvSC5DQkiQfIXCjUNt", 1864 | "fillStyle": "cross-hatch", 1865 | "strokeWidth": 2, 1866 | "strokeStyle": "solid", 1867 | "roughness": 0, 1868 | "opacity": 100, 1869 | "angle": 0, 1870 | "x": 1516.0465827194093, 1871 | "y": 530.5592355278228, 1872 | "strokeColor": "#000000", 1873 | "backgroundColor": "#ced4da", 1874 | "width": 55.78623035712741, 1875 | "height": 4.43935969434039e-13, 1876 | "seed": 615070064, 1877 | "groupIds": [ 1878 | "LlnIiHT0NvHZowiReLc0Y" 1879 | ], 1880 | "roundness": { 1881 | "type": 2 1882 | }, 1883 | "boundElements": [], 1884 | "updated": 1682873021995, 1885 | "link": null, 1886 | "locked": false, 1887 | "startBinding": null, 1888 | "endBinding": null, 1889 | "lastCommittedPoint": null, 1890 | "startArrowhead": null, 1891 | "endArrowhead": null, 1892 | "points": [ 1893 | [ 1894 | 0, 1895 | 0 1896 | ], 1897 | [ 1898 | 55.78623035712741, 1899 | 4.43935969434039e-13 1900 | ] 1901 | ] 1902 | }, 1903 | { 1904 | "type": "rectangle", 1905 | "version": 3933, 1906 | "versionNonce": 982961040, 1907 | "isDeleted": false, 1908 | "id": "3ZxkRQvUzKpnVN50xsz_j", 1909 | "fillStyle": "cross-hatch", 1910 | "strokeWidth": 2, 1911 | "strokeStyle": "solid", 1912 | "roughness": 0, 1913 | "opacity": 100, 1914 | "angle": 0, 1915 | "x": 1517.9201277803081, 1916 | "y": 537.4921078760159, 1917 | "strokeColor": "#000000", 1918 | "backgroundColor": "#ced4da", 1919 | "width": 52.92374702084933, 1920 | "height": 52.873067543499936, 1921 | "seed": 1344310128, 1922 | "groupIds": [ 1923 | "SshTHQT9_uT8_Ty3IF5S1", 1924 | "ExXb2VlookllGG1Rl64-z", 1925 | "_u7GLBJ_JRVsZ-CFjW6-e", 1926 | "PHTEawyJ0NOT_VC4iFOG8", 1927 | "HJb-a3ll1gPVm6twgxBYZ", 1928 | "9F1Elm8Gbavfgx30dsBQ_" 1929 | ], 1930 | "roundness": null, 1931 | "boundElements": [], 1932 | "updated": 1682873021995, 1933 | "link": null, 1934 | "locked": false 1935 | }, 1936 | { 1937 | "type": "line", 1938 | "version": 3167, 1939 | "versionNonce": 2008642928, 1940 | "isDeleted": false, 1941 | "id": "fzL1iF0uKAL-XuXufESso", 1942 | "fillStyle": "solid", 1943 | "strokeWidth": 2, 1944 | "strokeStyle": "solid", 1945 | "roughness": 0, 1946 | "opacity": 100, 1947 | "angle": 0, 1948 | "x": 1530.3523588024316, 1949 | "y": 566.5183238310941, 1950 | "strokeColor": "#0091e2", 1951 | "backgroundColor": "#0091e2", 1952 | "width": 46.48713442734936, 1953 | "height": 22.809000920556798, 1954 | "seed": 1960680816, 1955 | "groupIds": [ 1956 | "hmsn0YPjp_Y5alzviiy13", 1957 | "HJb-a3ll1gPVm6twgxBYZ", 1958 | "9F1Elm8Gbavfgx30dsBQ_" 1959 | ], 1960 | "roundness": { 1961 | "type": 2 1962 | }, 1963 | "boundElements": [], 1964 | "updated": 1682873021995, 1965 | "link": null, 1966 | "locked": false, 1967 | "startBinding": null, 1968 | "endBinding": null, 1969 | "lastCommittedPoint": null, 1970 | "startArrowhead": null, 1971 | "endArrowhead": null, 1972 | "points": [ 1973 | [ 1974 | 0, 1975 | 0 1976 | ], 1977 | [ 1978 | 21.75638446138932, 1979 | -0.4561081556793558 1980 | ], 1981 | [ 1982 | 26.986006374058764, 1983 | -1.1098081517203058 1984 | ], 1985 | [ 1986 | 25.90144967612373, 1987 | -5.314299391984548 1988 | ], 1989 | [ 1990 | 27.62484905327285, 1991 | -9.310795178962897 1992 | ], 1993 | [ 1994 | 30.284223481592495, 1995 | -5.581724860680294 1996 | ], 1997 | [ 1998 | 29.92766732841389, 1999 | -2.5806386288978684 2000 | ], 2001 | [ 2002 | 34.213874559328495, 2003 | -5.514869179267024 2004 | ], 2005 | [ 2006 | 37.56409875931184, 2007 | -4.2891801904850935 2008 | ], 2009 | [ 2010 | 35.57327067168915, 2011 | -1.5926602257905886 2012 | ], 2013 | [ 2014 | 31.190491380134898, 2015 | 0.47987687019200165 2016 | ], 2017 | [ 2018 | 19.75072075191394, 2019 | 12.253936877650219 2020 | ], 2021 | [ 2022 | 0.1396620199504468, 2023 | 13.498205741593903 2024 | ], 2025 | [ 2026 | -8.923035668037521, 2027 | 0.9151799944572949 2028 | ], 2029 | [ 2030 | -2.9416609543552075, 2031 | -0.4902749970307942 2032 | ], 2033 | [ 2034 | -0.23579100438095144, 2035 | -0.016047053939279094 2036 | ] 2037 | ] 2038 | }, 2039 | { 2040 | "type": "rectangle", 2041 | "version": 2072, 2042 | "versionNonce": 316070288, 2043 | "isDeleted": false, 2044 | "id": "eu8P7EOAWquAlkN1_9CYk", 2045 | "fillStyle": "solid", 2046 | "strokeWidth": 2, 2047 | "strokeStyle": "solid", 2048 | "roughness": 0, 2049 | "opacity": 100, 2050 | "angle": 0, 2051 | "x": 1525.1922964798462, 2052 | "y": 560.1043381828625, 2053 | "strokeColor": "#0091e2", 2054 | "backgroundColor": "#0091e2", 2055 | "width": 4.5700739194192055, 2056 | "height": 4.5700739194192055, 2057 | "seed": 1680371568, 2058 | "groupIds": [ 2059 | "hmsn0YPjp_Y5alzviiy13", 2060 | "HJb-a3ll1gPVm6twgxBYZ", 2061 | "9F1Elm8Gbavfgx30dsBQ_" 2062 | ], 2063 | "roundness": null, 2064 | "boundElements": [], 2065 | "updated": 1682873021995, 2066 | "link": null, 2067 | "locked": false 2068 | }, 2069 | { 2070 | "type": "rectangle", 2071 | "version": 2127, 2072 | "versionNonce": 1305036656, 2073 | "isDeleted": false, 2074 | "id": "smDkey3Bvw7K169Gt2SoR", 2075 | "fillStyle": "solid", 2076 | "strokeWidth": 2, 2077 | "strokeStyle": "solid", 2078 | "roughness": 0, 2079 | "opacity": 100, 2080 | "angle": 0, 2081 | "x": 1530.9048872810545, 2082 | "y": 559.8948798973953, 2083 | "strokeColor": "#0091e2", 2084 | "backgroundColor": "#0091e2", 2085 | "width": 4.5700739194192055, 2086 | "height": 4.5700739194192055, 2087 | "seed": 105447792, 2088 | "groupIds": [ 2089 | "hmsn0YPjp_Y5alzviiy13", 2090 | "HJb-a3ll1gPVm6twgxBYZ", 2091 | "9F1Elm8Gbavfgx30dsBQ_" 2092 | ], 2093 | "roundness": null, 2094 | "boundElements": [], 2095 | "updated": 1682873021995, 2096 | "link": null, 2097 | "locked": false 2098 | }, 2099 | { 2100 | "type": "rectangle", 2101 | "version": 2147, 2102 | "versionNonce": 1139091344, 2103 | "isDeleted": false, 2104 | "id": "gGdvlhhZSNEN7aiAy_Fcg", 2105 | "fillStyle": "solid", 2106 | "strokeWidth": 2, 2107 | "strokeStyle": "solid", 2108 | "roughness": 0, 2109 | "opacity": 100, 2110 | "angle": 0, 2111 | "x": 1536.8840607735638, 2112 | "y": 559.9900797828124, 2113 | "strokeColor": "#0091e2", 2114 | "backgroundColor": "#0091e2", 2115 | "width": 4.5700739194192055, 2116 | "height": 4.5700739194192055, 2117 | "seed": 2055689072, 2118 | "groupIds": [ 2119 | "hmsn0YPjp_Y5alzviiy13", 2120 | "HJb-a3ll1gPVm6twgxBYZ", 2121 | "9F1Elm8Gbavfgx30dsBQ_" 2122 | ], 2123 | "roundness": null, 2124 | "boundElements": [], 2125 | "updated": 1682873021995, 2126 | "link": null, 2127 | "locked": false 2128 | }, 2129 | { 2130 | "type": "rectangle", 2131 | "version": 2168, 2132 | "versionNonce": 906942832, 2133 | "isDeleted": false, 2134 | "id": "M8mp16lesP5w9mhyJLQVq", 2135 | "fillStyle": "solid", 2136 | "strokeWidth": 2, 2137 | "strokeStyle": "solid", 2138 | "roughness": 0, 2139 | "opacity": 100, 2140 | "angle": 0, 2141 | "x": 1542.8441917320888, 2142 | "y": 559.7044609497847, 2143 | "strokeColor": "#0091e2", 2144 | "backgroundColor": "#0091e2", 2145 | "width": 4.5700739194192055, 2146 | "height": 4.5700739194192055, 2147 | "seed": 925590896, 2148 | "groupIds": [ 2149 | "hmsn0YPjp_Y5alzviiy13", 2150 | "HJb-a3ll1gPVm6twgxBYZ", 2151 | "9F1Elm8Gbavfgx30dsBQ_" 2152 | ], 2153 | "roundness": null, 2154 | "boundElements": [], 2155 | "updated": 1682873021995, 2156 | "link": null, 2157 | "locked": false 2158 | }, 2159 | { 2160 | "type": "rectangle", 2161 | "version": 2210, 2162 | "versionNonce": 435795344, 2163 | "isDeleted": false, 2164 | "id": "5eNcxMOEiPpR2VA-j4UHp", 2165 | "fillStyle": "solid", 2166 | "strokeWidth": 2, 2167 | "strokeStyle": "solid", 2168 | "roughness": 0, 2169 | "opacity": 100, 2170 | "angle": 0, 2171 | "x": 1548.4996517352095, 2172 | "y": 559.7806246934714, 2173 | "strokeColor": "#0091e2", 2174 | "backgroundColor": "#0091e2", 2175 | "width": 4.5700739194192055, 2176 | "height": 4.5700739194192055, 2177 | "seed": 696072048, 2178 | "groupIds": [ 2179 | "hmsn0YPjp_Y5alzviiy13", 2180 | "HJb-a3ll1gPVm6twgxBYZ", 2181 | "9F1Elm8Gbavfgx30dsBQ_" 2182 | ], 2183 | "roundness": null, 2184 | "boundElements": [], 2185 | "updated": 1682873021995, 2186 | "link": null, 2187 | "locked": false 2188 | }, 2189 | { 2190 | "type": "rectangle", 2191 | "version": 2114, 2192 | "versionNonce": 1674718064, 2193 | "isDeleted": false, 2194 | "id": "WKuqAF_XTzN_FZPDzO3rG", 2195 | "fillStyle": "solid", 2196 | "strokeWidth": 2, 2197 | "strokeStyle": "solid", 2198 | "roughness": 0, 2199 | "opacity": 100, 2200 | "angle": 0, 2201 | "x": 1530.7430241441027, 2202 | "y": 554.3536750981947, 2203 | "strokeColor": "#0091e2", 2204 | "backgroundColor": "#0091e2", 2205 | "width": 4.5700739194192055, 2206 | "height": 4.5700739194192055, 2207 | "seed": 1438959984, 2208 | "groupIds": [ 2209 | "hmsn0YPjp_Y5alzviiy13", 2210 | "HJb-a3ll1gPVm6twgxBYZ", 2211 | "9F1Elm8Gbavfgx30dsBQ_" 2212 | ], 2213 | "roundness": null, 2214 | "boundElements": [], 2215 | "updated": 1682873021995, 2216 | "link": null, 2217 | "locked": false 2218 | }, 2219 | { 2220 | "type": "rectangle", 2221 | "version": 2138, 2222 | "versionNonce": 441222032, 2223 | "isDeleted": false, 2224 | "id": "NRe7fklkaVzDF7n5oVKL7", 2225 | "fillStyle": "solid", 2226 | "strokeWidth": 2, 2227 | "strokeStyle": "solid", 2228 | "roughness": 0, 2229 | "opacity": 100, 2230 | "angle": 0, 2231 | "x": 1536.7412433667287, 2232 | "y": 554.4298420380095, 2233 | "strokeColor": "#0091e2", 2234 | "backgroundColor": "#0091e2", 2235 | "width": 4.5700739194192055, 2236 | "height": 4.5700739194192055, 2237 | "seed": 2001345392, 2238 | "groupIds": [ 2239 | "hmsn0YPjp_Y5alzviiy13", 2240 | "HJb-a3ll1gPVm6twgxBYZ", 2241 | "9F1Elm8Gbavfgx30dsBQ_" 2242 | ], 2243 | "roundness": null, 2244 | "boundElements": [], 2245 | "updated": 1682873021995, 2246 | "link": null, 2247 | "locked": false 2248 | }, 2249 | { 2250 | "type": "rectangle", 2251 | "version": 2162, 2252 | "versionNonce": 1633369456, 2253 | "isDeleted": false, 2254 | "id": "4Q-FbJizyaFYbs8qBR_QG", 2255 | "fillStyle": "solid", 2256 | "strokeWidth": 2, 2257 | "strokeStyle": "solid", 2258 | "roughness": 0, 2259 | "opacity": 100, 2260 | "angle": 0, 2261 | "x": 1542.815629529172, 2262 | "y": 554.4298356457563, 2263 | "strokeColor": "#0091e2", 2264 | "backgroundColor": "#0091e2", 2265 | "width": 4.5700739194192055, 2266 | "height": 4.5700739194192055, 2267 | "seed": 542453104, 2268 | "groupIds": [ 2269 | "hmsn0YPjp_Y5alzviiy13", 2270 | "HJb-a3ll1gPVm6twgxBYZ", 2271 | "9F1Elm8Gbavfgx30dsBQ_" 2272 | ], 2273 | "roundness": null, 2274 | "boundElements": [], 2275 | "updated": 1682873021995, 2276 | "link": null, 2277 | "locked": false 2278 | }, 2279 | { 2280 | "type": "rectangle", 2281 | "version": 2181, 2282 | "versionNonce": 1662357904, 2283 | "isDeleted": false, 2284 | "id": "XB80Ln6Z5oyDBIQCMcd-Y", 2285 | "fillStyle": "solid", 2286 | "strokeWidth": 2, 2287 | "strokeStyle": "solid", 2288 | "roughness": 0, 2289 | "opacity": 100, 2290 | "angle": 0, 2291 | "x": 1542.6156813242476, 2292 | "y": 548.6029960328846, 2293 | "strokeColor": "#0091e2", 2294 | "backgroundColor": "#0091e2", 2295 | "width": 4.5700739194192055, 2296 | "height": 4.5700739194192055, 2297 | "seed": 1512963952, 2298 | "groupIds": [ 2299 | "hmsn0YPjp_Y5alzviiy13", 2300 | "HJb-a3ll1gPVm6twgxBYZ", 2301 | "9F1Elm8Gbavfgx30dsBQ_" 2302 | ], 2303 | "roundness": null, 2304 | "boundElements": [], 2305 | "updated": 1682873021995, 2306 | "link": null, 2307 | "locked": false 2308 | }, 2309 | { 2310 | "type": "text", 2311 | "version": 2693, 2312 | "versionNonce": 1464356001, 2313 | "isDeleted": false, 2314 | "id": "ty4_D7EYS9feQhHOj2MTL", 2315 | "fillStyle": "hachure", 2316 | "strokeWidth": 1, 2317 | "strokeStyle": "solid", 2318 | "roughness": 1, 2319 | "opacity": 100, 2320 | "angle": 0, 2321 | "x": 1516.8779083579748, 2322 | "y": 594.7862226218559, 2323 | "strokeColor": "#000000", 2324 | "backgroundColor": "transparent", 2325 | "width": 54.233333587646484, 2326 | "height": 20.525442487132864, 2327 | "seed": 1159500144, 2328 | "groupIds": [ 2329 | "9F1Elm8Gbavfgx30dsBQ_" 2330 | ], 2331 | "roundness": null, 2332 | "boundElements": [], 2333 | "updated": 1682936189434, 2334 | "link": null, 2335 | "locked": false, 2336 | "fontSize": 16.420353989706292, 2337 | "fontFamily": 1, 2338 | "text": "Docker", 2339 | "textAlign": "left", 2340 | "verticalAlign": "top", 2341 | "containerId": null, 2342 | "originalText": "Docker", 2343 | "lineHeight": 1.25, 2344 | "baseline": 15 2345 | }, 2346 | { 2347 | "type": "line", 2348 | "version": 3428, 2349 | "versionNonce": 193781648, 2350 | "isDeleted": false, 2351 | "id": "BNo3uankt7waIAoZrYRyl", 2352 | "fillStyle": "solid", 2353 | "strokeWidth": 2, 2354 | "strokeStyle": "solid", 2355 | "roughness": 0, 2356 | "opacity": 100, 2357 | "angle": 0, 2358 | "x": 1011.0131828824956, 2359 | "y": 553.7248950964641, 2360 | "strokeColor": "#495057", 2361 | "backgroundColor": "#ced4da", 2362 | "width": 67.51988661992566, 2363 | "height": 42.52159828877349, 2364 | "seed": 2048083824, 2365 | "groupIds": [ 2366 | "3Q0metPBC1WYI73cInfmV" 2367 | ], 2368 | "roundness": { 2369 | "type": 2 2370 | }, 2371 | "boundElements": [], 2372 | "updated": 1682873021995, 2373 | "link": null, 2374 | "locked": false, 2375 | "startBinding": null, 2376 | "endBinding": null, 2377 | "lastCommittedPoint": null, 2378 | "startArrowhead": null, 2379 | "endArrowhead": null, 2380 | "points": [ 2381 | [ 2382 | 0, 2383 | 0 2384 | ], 2385 | [ 2386 | -3.492407928616922, 2387 | 3.186056355930977 2388 | ], 2389 | [ 2390 | -3.7374891867654236, 2391 | 7.5668838453363545 2392 | ], 2393 | [ 2394 | -3.2487919337338624, 2395 | 17.707120901232642 2396 | ], 2397 | [ 2398 | -1.9606500651884953, 2399 | 23.03763826596361 2400 | ], 2401 | [ 2402 | 1.1958700809232037, 2403 | 27.142749339952157 2404 | ], 2405 | [ 2406 | 5.800575559070293, 2407 | 31.3549418655951 2408 | ], 2409 | [ 2410 | 11.452762075121491, 2411 | 34.05767874466663 2412 | ], 2413 | [ 2414 | 18.01347247391849, 2415 | 35.904404318762595 2416 | ], 2417 | [ 2418 | 28.061804058008835, 2419 | 37.12981060950537 2420 | ], 2421 | [ 2422 | 37.86505438395099, 2423 | 36.70091840774548 2424 | ], 2425 | [ 2426 | 48.83244068609838, 2427 | 33.88523242218446 2428 | ], 2429 | [ 2430 | 57.62582976962708, 2431 | 28.947659408082156 2432 | ], 2433 | [ 2434 | 62.98515071254394, 2435 | 22.51500901331588 2436 | ], 2437 | [ 2438 | 63.78239743316023, 2439 | 17.58458027215863 2440 | ], 2441 | [ 2442 | 63.75176227589162, 2443 | 3.1554211986624443 2444 | ], 2445 | [ 2446 | 61.39285516621165, 2447 | 0.06127031453706655 2448 | ], 2449 | [ 2450 | 53.305173647309594, 2451 | -1.2254062907428291 2452 | ], 2453 | [ 2454 | 12.31533322196453, 2455 | -5.391787679268121 2456 | ], 2457 | [ 2458 | 0, 2459 | 0 2460 | ] 2461 | ] 2462 | }, 2463 | { 2464 | "type": "ellipse", 2465 | "version": 1597, 2466 | "versionNonce": 1742643568, 2467 | "isDeleted": false, 2468 | "id": "gdn9Ve3S21ColMGOXhqWX", 2469 | "fillStyle": "solid", 2470 | "strokeWidth": 2, 2471 | "strokeStyle": "solid", 2472 | "roughness": 0, 2473 | "opacity": 100, 2474 | "angle": 0, 2475 | "x": 1007.0116855176716, 2476 | "y": 536.0770232786555, 2477 | "strokeColor": "#495057", 2478 | "backgroundColor": "#ced4da", 2479 | "width": 67.97662896465694, 2480 | "height": 42.710979260978974, 2481 | "seed": 1226365296, 2482 | "groupIds": [ 2483 | "3Q0metPBC1WYI73cInfmV" 2484 | ], 2485 | "roundness": null, 2486 | "boundElements": [ 2487 | { 2488 | "id": "h2fiyrVjBZW9hFVpdYGPZ", 2489 | "type": "arrow" 2490 | }, 2491 | { 2492 | "id": "IFLo1dVYjrxbjleYs7-B1", 2493 | "type": "arrow" 2494 | } 2495 | ], 2496 | "updated": 1682873021995, 2497 | "link": null, 2498 | "locked": false 2499 | }, 2500 | { 2501 | "type": "line", 2502 | "version": 2819, 2503 | "versionNonce": 1969634192, 2504 | "isDeleted": false, 2505 | "id": "WPvEF5aDaQv2m-vAU4ln2", 2506 | "fillStyle": "solid", 2507 | "strokeWidth": 1, 2508 | "strokeStyle": "solid", 2509 | "roughness": 0, 2510 | "opacity": 100, 2511 | "angle": 3.141592653589793, 2512 | "x": 1031.9112575790132, 2513 | "y": 554.483696297685, 2514 | "strokeColor": "#495057", 2515 | "backgroundColor": "#495057", 2516 | "width": 16.679910465140814, 2517 | "height": 12.67339451086911, 2518 | "seed": 94120816, 2519 | "groupIds": [ 2520 | "W_Dg39_0rAxmQ-4QueE6T", 2521 | "G6hqWCpc3L62B16y9Zckx", 2522 | "4dV40nR9AlzHs6GILel97", 2523 | "3Q0metPBC1WYI73cInfmV" 2524 | ], 2525 | "roundness": null, 2526 | "boundElements": [], 2527 | "updated": 1682873021995, 2528 | "link": null, 2529 | "locked": false, 2530 | "startBinding": null, 2531 | "endBinding": null, 2532 | "lastCommittedPoint": null, 2533 | "startArrowhead": null, 2534 | "endArrowhead": null, 2535 | "points": [ 2536 | [ 2537 | 0, 2538 | 0 2539 | ], 2540 | [ 2541 | -7.746360818647345, 2542 | -6.278424194165094 2543 | ], 2544 | [ 2545 | -11.670181423616022, 2546 | -4.003483654025771 2547 | ], 2548 | [ 2549 | -11.756313474661507, 2550 | -12.67339451086911 2551 | ], 2552 | [ 2553 | -0.8348680936116399, 2554 | -11.87656218528622 2555 | ], 2556 | [ 2557 | -4.506171432839819, 2558 | -9.045871827168456 2559 | ], 2560 | [ 2561 | 4.923596990479309, 2562 | -2.355297308908259 2563 | ], 2564 | [ 2565 | 0, 2566 | 0 2567 | ] 2568 | ] 2569 | }, 2570 | { 2571 | "type": "line", 2572 | "version": 2672, 2573 | "versionNonce": 668534128, 2574 | "isDeleted": false, 2575 | "id": "cggqYAxg33e86GRrpr1q8", 2576 | "fillStyle": "solid", 2577 | "strokeWidth": 1, 2578 | "strokeStyle": "solid", 2579 | "roughness": 0, 2580 | "opacity": 100, 2581 | "angle": 0, 2582 | "x": 1055.5773799235985, 2583 | "y": 572.5891393999113, 2584 | "strokeColor": "#495057", 2585 | "backgroundColor": "#495057", 2586 | "width": 16.11559478975416, 2587 | "height": 12.96922317330706, 2588 | "seed": 971324784, 2589 | "groupIds": [ 2590 | "DcgwM5PSmju7FOHHYxJLU", 2591 | "9ZJJCbxmzBkJ0wgLxWBxw", 2592 | "BkVSkMrgGJMgzJFeoGpk_", 2593 | "3Q0metPBC1WYI73cInfmV" 2594 | ], 2595 | "roundness": null, 2596 | "boundElements": [], 2597 | "updated": 1682873021995, 2598 | "link": null, 2599 | "locked": false, 2600 | "startBinding": null, 2601 | "endBinding": null, 2602 | "lastCommittedPoint": null, 2603 | "startArrowhead": null, 2604 | "endArrowhead": null, 2605 | "points": [ 2606 | [ 2607 | 0, 2608 | 0 2609 | ], 2610 | [ 2611 | -8.53284305418864, 2612 | -6.693352352311601 2613 | ], 2614 | [ 2615 | -12.383586688951164, 2616 | -3.2431634395556355 2617 | ], 2618 | [ 2619 | -12.468114622489345, 2620 | -12.96922317330706 2621 | ], 2622 | [ 2623 | -1.7500695033443732, 2624 | -12.187230999193366 2625 | ], 2626 | [ 2627 | -5.35299873688629, 2628 | -9.409259227318111 2629 | ], 2630 | [ 2631 | 3.6474801672648143, 2632 | -3.3506298140325192 2633 | ], 2634 | [ 2635 | 0, 2636 | 0 2637 | ] 2638 | ] 2639 | }, 2640 | { 2641 | "type": "line", 2642 | "version": 2887, 2643 | "versionNonce": 574114192, 2644 | "isDeleted": false, 2645 | "id": "D6lV0_ft4gya4y-x-25x4", 2646 | "fillStyle": "solid", 2647 | "strokeWidth": 1, 2648 | "strokeStyle": "solid", 2649 | "roughness": 0, 2650 | "opacity": 100, 2651 | "angle": 3.141592653589793, 2652 | "x": 1024.7709027109172, 2653 | "y": 571.9330289555394, 2654 | "strokeColor": "#495057", 2655 | "backgroundColor": "#495057", 2656 | "width": 16.11559478975416, 2657 | "height": 12.96922317330706, 2658 | "seed": 1731989360, 2659 | "groupIds": [ 2660 | "BRZFNjxPyFfAEaWKtplEM", 2661 | "z2G_HK4pP2TNGXU_pw1am", 2662 | "3xCyZYjVen9Kn38NjhGQ9", 2663 | "3Q0metPBC1WYI73cInfmV" 2664 | ], 2665 | "roundness": null, 2666 | "boundElements": [], 2667 | "updated": 1682873021995, 2668 | "link": null, 2669 | "locked": false, 2670 | "startBinding": null, 2671 | "endBinding": null, 2672 | "lastCommittedPoint": null, 2673 | "startArrowhead": null, 2674 | "endArrowhead": null, 2675 | "points": [ 2676 | [ 2677 | 0, 2678 | 0 2679 | ], 2680 | [ 2681 | 8.53284305418862, 2682 | -6.693352352311617 2683 | ], 2684 | [ 2685 | 12.38358668895114, 2686 | -3.2431634395556337 2687 | ], 2688 | [ 2689 | 12.468114622489335, 2690 | -12.96922317330706 2691 | ], 2692 | [ 2693 | 1.7500695033443556, 2694 | -12.187230999193353 2695 | ], 2696 | [ 2697 | 5.352998736886278, 2698 | -9.409259227318092 2699 | ], 2700 | [ 2701 | -3.6474801672648236, 2702 | -3.3506298140325406 2703 | ], 2704 | [ 2705 | 0, 2706 | 0 2707 | ] 2708 | ] 2709 | }, 2710 | { 2711 | "type": "line", 2712 | "version": 2932, 2713 | "versionNonce": 1937168240, 2714 | "isDeleted": false, 2715 | "id": "4MQYPVz-zTh8kd6YtnpzY", 2716 | "fillStyle": "solid", 2717 | "strokeWidth": 1, 2718 | "strokeStyle": "solid", 2719 | "roughness": 0, 2720 | "opacity": 100, 2721 | "angle": 0, 2722 | "x": 1045.300743203833, 2723 | "y": 555.7276873305968, 2724 | "strokeColor": "#495057", 2725 | "backgroundColor": "#495057", 2726 | "width": 16.11559478975416, 2727 | "height": 12.96922317330706, 2728 | "seed": 636002672, 2729 | "groupIds": [ 2730 | "uFw01obAD5swCyQ8b94Ps", 2731 | "42QBLLREGn9WI8D5wZViC", 2732 | "QSjZXIuO55IGTPZXe5o23", 2733 | "3Q0metPBC1WYI73cInfmV" 2734 | ], 2735 | "roundness": null, 2736 | "boundElements": [], 2737 | "updated": 1682873021995, 2738 | "link": null, 2739 | "locked": false, 2740 | "startBinding": null, 2741 | "endBinding": null, 2742 | "lastCommittedPoint": null, 2743 | "startArrowhead": null, 2744 | "endArrowhead": null, 2745 | "points": [ 2746 | [ 2747 | 0, 2748 | 0 2749 | ], 2750 | [ 2751 | 8.53284305418862, 2752 | -6.693352352311617 2753 | ], 2754 | [ 2755 | 12.38358668895114, 2756 | -3.2431634395556337 2757 | ], 2758 | [ 2759 | 12.468114622489335, 2760 | -12.96922317330706 2761 | ], 2762 | [ 2763 | 1.7500695033443556, 2764 | -12.187230999193353 2765 | ], 2766 | [ 2767 | 5.352998736886278, 2768 | -9.409259227318092 2769 | ], 2770 | [ 2771 | -3.6474801672648236, 2772 | -3.3506298140325406 2773 | ], 2774 | [ 2775 | 0, 2776 | 0 2777 | ] 2778 | ] 2779 | }, 2780 | { 2781 | "type": "text", 2782 | "version": 815, 2783 | "versionNonce": 978936321, 2784 | "isDeleted": false, 2785 | "id": "SIGpfDjYquc4N4ZHXi3u1", 2786 | "fillStyle": "hachure", 2787 | "strokeWidth": 1, 2788 | "strokeStyle": "solid", 2789 | "roughness": 1, 2790 | "opacity": 100, 2791 | "angle": 0, 2792 | "x": 920.8288143003726, 2793 | "y": 484, 2794 | "strokeColor": "#000000", 2795 | "backgroundColor": "transparent", 2796 | "width": 47.96666717529297, 2797 | "height": 49.99999999999999, 2798 | "seed": 2037363600, 2799 | "groupIds": [], 2800 | "roundness": null, 2801 | "boundElements": [], 2802 | "updated": 1682936232079, 2803 | "link": null, 2804 | "locked": false, 2805 | "fontSize": 19.999999999999996, 2806 | "fontFamily": 1, 2807 | "text": "LAN/\nNAT", 2808 | "textAlign": "left", 2809 | "verticalAlign": "top", 2810 | "containerId": null, 2811 | "originalText": "LAN/\nNAT", 2812 | "lineHeight": 1.25, 2813 | "baseline": 43 2814 | }, 2815 | { 2816 | "type": "text", 2817 | "version": 422, 2818 | "versionNonce": 951586945, 2819 | "isDeleted": false, 2820 | "id": "Ld1cY0niU7UsvTgon1YzK", 2821 | "fillStyle": "hachure", 2822 | "strokeWidth": 1, 2823 | "strokeStyle": "solid", 2824 | "roughness": 1, 2825 | "opacity": 100, 2826 | "angle": 0, 2827 | "x": 1188, 2828 | "y": 654, 2829 | "strokeColor": "#000000", 2830 | "backgroundColor": "transparent", 2831 | "width": 172.23333740234375, 2832 | "height": 70, 2833 | "seed": 340365680, 2834 | "groupIds": [], 2835 | "roundness": null, 2836 | "boundElements": [], 2837 | "updated": 1682936189436, 2838 | "link": null, 2839 | "locked": false, 2840 | "fontSize": 28, 2841 | "fontFamily": 1, 2842 | "text": "Possible ISP\ndouble NAT", 2843 | "textAlign": "left", 2844 | "verticalAlign": "top", 2845 | "containerId": null, 2846 | "originalText": "Possible ISP\ndouble NAT", 2847 | "lineHeight": 1.25, 2848 | "baseline": 60 2849 | }, 2850 | { 2851 | "type": "arrow", 2852 | "version": 1872, 2853 | "versionNonce": 685159777, 2854 | "isDeleted": false, 2855 | "id": "h2fiyrVjBZW9hFVpdYGPZ", 2856 | "fillStyle": "hachure", 2857 | "strokeWidth": 1, 2858 | "strokeStyle": "solid", 2859 | "roughness": 1, 2860 | "opacity": 100, 2861 | "angle": 0, 2862 | "x": 902.0000000000001, 2863 | "y": 559.1929163118834, 2864 | "strokeColor": "#1864ab", 2865 | "backgroundColor": "transparent", 2866 | "width": 96.32283492894351, 2867 | "height": 1.3634160073581825, 2868 | "seed": 242824592, 2869 | "groupIds": [], 2870 | "roundness": { 2871 | "type": 2 2872 | }, 2873 | "boundElements": [], 2874 | "updated": 1682936328723, 2875 | "link": null, 2876 | "locked": false, 2877 | "startBinding": { 2878 | "elementId": "BPGgjUzZW_m4V3-yse6UM", 2879 | "focus": 0.14117277820383548, 2880 | "gap": 14.615468046072976 2881 | }, 2882 | "endBinding": { 2883 | "elementId": "gdn9Ve3S21ColMGOXhqWX", 2884 | "focus": -0.17451972329752424, 2885 | "gap": 8.91788508849858 2886 | }, 2887 | "lastCommittedPoint": null, 2888 | "startArrowhead": "arrow", 2889 | "endArrowhead": "arrow", 2890 | "points": [ 2891 | [ 2892 | 0, 2893 | 0 2894 | ], 2895 | [ 2896 | 96.32283492894351, 2897 | 1.3634160073581825 2898 | ] 2899 | ] 2900 | }, 2901 | { 2902 | "type": "arrow", 2903 | "version": 1640, 2904 | "versionNonce": 1215691120, 2905 | "isDeleted": false, 2906 | "id": "IFLo1dVYjrxbjleYs7-B1", 2907 | "fillStyle": "hachure", 2908 | "strokeWidth": 1, 2909 | "strokeStyle": "solid", 2910 | "roughness": 1, 2911 | "opacity": 100, 2912 | "angle": 0, 2913 | "x": 1084.6209138792515, 2914 | "y": 554.668296037602, 2915 | "strokeColor": "#1864ab", 2916 | "backgroundColor": "transparent", 2917 | "width": 410.055520825219, 2918 | "height": 1.2681788381873957, 2919 | "seed": 418889584, 2920 | "groupIds": [], 2921 | "roundness": { 2922 | "type": 2 2923 | }, 2924 | "boundElements": [], 2925 | "updated": 1682873022524, 2926 | "link": null, 2927 | "locked": false, 2928 | "startBinding": { 2929 | "elementId": "gdn9Ve3S21ColMGOXhqWX", 2930 | "focus": -0.13575376713769033, 2931 | "gap": 9.797543855620297 2932 | }, 2933 | "endBinding": { 2934 | "elementId": "VDqoMW0Zg79rD8Mjk9cue", 2935 | "focus": -0.14922755818280933, 2936 | "gap": 12.941241149578104 2937 | }, 2938 | "lastCommittedPoint": null, 2939 | "startArrowhead": "arrow", 2940 | "endArrowhead": "arrow", 2941 | "points": [ 2942 | [ 2943 | 0, 2944 | 0 2945 | ], 2946 | [ 2947 | 410.055520825219, 2948 | 1.2681788381873957 2949 | ] 2950 | ] 2951 | }, 2952 | { 2953 | "type": "arrow", 2954 | "version": 1886, 2955 | "versionNonce": 487888272, 2956 | "isDeleted": false, 2957 | "id": "s6q4bBRN01Us9PnG6-d8_", 2958 | "fillStyle": "hachure", 2959 | "strokeWidth": 1, 2960 | "strokeStyle": "solid", 2961 | "roughness": 1, 2962 | "opacity": 100, 2963 | "angle": 0, 2964 | "x": 1594.7639732662064, 2965 | "y": 558.1658666763484, 2966 | "strokeColor": "#1864ab", 2967 | "backgroundColor": "transparent", 2968 | "width": 209.0845675000005, 2969 | "height": 0.9369470439258976, 2970 | "seed": 791143792, 2971 | "groupIds": [], 2972 | "roundness": { 2973 | "type": 2 2974 | }, 2975 | "boundElements": [], 2976 | "updated": 1682873022524, 2977 | "link": null, 2978 | "locked": false, 2979 | "startBinding": { 2980 | "elementId": "VDqoMW0Zg79rD8Mjk9cue", 2981 | "focus": 0.17628273703004427, 2982 | "gap": 14.381649120252291 2983 | }, 2984 | "endBinding": null, 2985 | "lastCommittedPoint": null, 2986 | "startArrowhead": "arrow", 2987 | "endArrowhead": "arrow", 2988 | "points": [ 2989 | [ 2990 | 0, 2991 | 0 2992 | ], 2993 | [ 2994 | 209.0845675000005, 2995 | 0.9369470439258976 2996 | ] 2997 | ] 2998 | }, 2999 | { 3000 | "type": "text", 3001 | "version": 513, 3002 | "versionNonce": 1525675023, 3003 | "isDeleted": false, 3004 | "id": "DwXRnsIAwsoSjfco1rwJ_", 3005 | "fillStyle": "hachure", 3006 | "strokeWidth": 1, 3007 | "strokeStyle": "solid", 3008 | "roughness": 1, 3009 | "opacity": 100, 3010 | "angle": 0, 3011 | "x": 998, 3012 | "y": 591, 3013 | "strokeColor": "#000000", 3014 | "backgroundColor": "transparent", 3015 | "width": 93.56666564941406, 3016 | "height": 35, 3017 | "seed": 409929584, 3018 | "groupIds": [], 3019 | "roundness": null, 3020 | "boundElements": [ 3021 | { 3022 | "id": "h2fiyrVjBZW9hFVpdYGPZ", 3023 | "type": "arrow" 3024 | } 3025 | ], 3026 | "updated": 1682936189436, 3027 | "link": null, 3028 | "locked": false, 3029 | "fontSize": 28, 3030 | "fontFamily": 1, 3031 | "text": "Router", 3032 | "textAlign": "left", 3033 | "verticalAlign": "top", 3034 | "containerId": null, 3035 | "originalText": "Router", 3036 | "lineHeight": 1.25, 3037 | "baseline": 25 3038 | }, 3039 | { 3040 | "type": "text", 3041 | "version": 1609, 3042 | "versionNonce": 54810721, 3043 | "isDeleted": false, 3044 | "id": "9SxWIUhYTvpFZxAqvS6bE", 3045 | "fillStyle": "solid", 3046 | "strokeWidth": 1, 3047 | "strokeStyle": "solid", 3048 | "roughness": 1, 3049 | "opacity": 100, 3050 | "angle": 0, 3051 | "x": 1803.9361897406307, 3052 | "y": 592.4844628405266, 3053 | "strokeColor": "#000000", 3054 | "backgroundColor": "white", 3055 | "width": 85.0999984741211, 3056 | "height": 47.627011897514286, 3057 | "seed": 1936907632, 3058 | "groupIds": [ 3059 | "oj-q_aW0-18ZnIOrUekof" 3060 | ], 3061 | "roundness": null, 3062 | "boundElements": [], 3063 | "updated": 1682936189436, 3064 | "link": null, 3065 | "locked": false, 3066 | "fontSize": 38.10160951801143, 3067 | "fontFamily": 1, 3068 | "text": "User", 3069 | "textAlign": "left", 3070 | "verticalAlign": "top", 3071 | "containerId": null, 3072 | "originalText": "User", 3073 | "lineHeight": 1.25, 3074 | "baseline": 34 3075 | }, 3076 | { 3077 | "type": "line", 3078 | "version": 1932, 3079 | "versionNonce": 173514608, 3080 | "isDeleted": false, 3081 | "id": "UeqmC44WILXnEtZCz56w_", 3082 | "fillStyle": "cross-hatch", 3083 | "strokeWidth": 2, 3084 | "strokeStyle": "solid", 3085 | "roughness": 0, 3086 | "opacity": 100, 3087 | "angle": 0, 3088 | "x": 1813.180354797371, 3089 | "y": 571.8016553484773, 3090 | "strokeColor": "#000000", 3091 | "backgroundColor": "#ced4da", 3092 | "width": 62.776073682831026, 3093 | "height": 55.9002907862172, 3094 | "seed": 1578479472, 3095 | "groupIds": [ 3096 | "2WG6BWax24wP1Fnp_ani9", 3097 | "oj-q_aW0-18ZnIOrUekof" 3098 | ], 3099 | "roundness": { 3100 | "type": 2 3101 | }, 3102 | "boundElements": [], 3103 | "updated": 1682873021995, 3104 | "link": null, 3105 | "locked": false, 3106 | "startBinding": null, 3107 | "endBinding": null, 3108 | "lastCommittedPoint": null, 3109 | "startArrowhead": null, 3110 | "endArrowhead": null, 3111 | "points": [ 3112 | [ 3113 | 0, 3114 | 0 3115 | ], 3116 | [ 3117 | 7.0309202524770695, 3118 | -37.04236136436079 3119 | ], 3120 | [ 3121 | 30.13251536775889, 3122 | -55.9002907862172 3123 | ], 3124 | [ 3125 | 53.23411048304071, 3126 | -41.08334624047283 3127 | ], 3128 | [ 3129 | 62.776073682831026, 3130 | -3.846379825811827 3131 | ], 3132 | [ 3133 | 0, 3134 | 0 3135 | ] 3136 | ] 3137 | }, 3138 | { 3139 | "type": "ellipse", 3140 | "version": 1669, 3141 | "versionNonce": 537415568, 3142 | "isDeleted": false, 3143 | "id": "oyAvCyX9oA-ZibVyQUJk6", 3144 | "fillStyle": "cross-hatch", 3145 | "strokeWidth": 2, 3146 | "strokeStyle": "solid", 3147 | "roughness": 0, 3148 | "opacity": 100, 3149 | "angle": 0, 3150 | "x": 1829.0483130940513, 3151 | "y": 486.8280131020824, 3152 | "strokeColor": "#000000", 3153 | "backgroundColor": "#ced4da", 3154 | "width": 32.141349725609516, 3155 | "height": 28.12368100990842, 3156 | "seed": 1351500144, 3157 | "groupIds": [ 3158 | "2WG6BWax24wP1Fnp_ani9", 3159 | "oj-q_aW0-18ZnIOrUekof" 3160 | ], 3161 | "roundness": null, 3162 | "boundElements": [], 3163 | "updated": 1682873021995, 3164 | "link": null, 3165 | "locked": false 3166 | }, 3167 | { 3168 | "type": "text", 3169 | "version": 141, 3170 | "versionNonce": 678729025, 3171 | "isDeleted": false, 3172 | "id": "-44M-dLbYlPWYs4ZEp5Uh", 3173 | "fillStyle": "hachure", 3174 | "strokeWidth": 1, 3175 | "strokeStyle": "solid", 3176 | "roughness": 1, 3177 | "opacity": 100, 3178 | "angle": 0, 3179 | "x": 765, 3180 | "y": 679, 3181 | "strokeColor": "#000000", 3182 | "backgroundColor": "transparent", 3183 | "width": 171.76666259765625, 3184 | "height": 25, 3185 | "seed": 2011286384, 3186 | "groupIds": [], 3187 | "roundness": null, 3188 | "boundElements": [], 3189 | "updated": 1682936415164, 3190 | "link": null, 3191 | "locked": false, 3192 | "fontSize": 20, 3193 | "fontFamily": 1, 3194 | "text": "(Peer private IP)", 3195 | "textAlign": "left", 3196 | "verticalAlign": "top", 3197 | "containerId": null, 3198 | "originalText": "(Peer private IP)", 3199 | "lineHeight": 1.25, 3200 | "baseline": 18 3201 | }, 3202 | { 3203 | "id": "uF4ruSHgCrDlEpQ_knyni", 3204 | "type": "text", 3205 | "x": 1494, 3206 | "y": 684, 3207 | "width": 103.63333129882812, 3208 | "height": 25, 3209 | "angle": 0, 3210 | "strokeColor": "#000000", 3211 | "backgroundColor": "transparent", 3212 | "fillStyle": "hachure", 3213 | "strokeWidth": 1, 3214 | "strokeStyle": "solid", 3215 | "roughness": 1, 3216 | "opacity": 100, 3217 | "groupIds": [], 3218 | "roundness": null, 3219 | "seed": 935295457, 3220 | "version": 35, 3221 | "versionNonce": 1003574177, 3222 | "isDeleted": false, 3223 | "boundElements": null, 3224 | "updated": 1682936278289, 3225 | "link": null, 3226 | "locked": false, 3227 | "text": "(Public IP)", 3228 | "fontSize": 20, 3229 | "fontFamily": 1, 3230 | "textAlign": "left", 3231 | "verticalAlign": "top", 3232 | "baseline": 18, 3233 | "containerId": null, 3234 | "originalText": "(Public IP)", 3235 | "lineHeight": 1.25 3236 | } 3237 | ], 3238 | "appState": { 3239 | "gridSize": null, 3240 | "viewBackgroundColor": "#ffffff" 3241 | }, 3242 | "files": {} 3243 | } -------------------------------------------------------------------------------- /docker-wireguard-tunnel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DigitallyRefined/docker-wireguard-tunnel/3bb4f9e92090c02c2ab647566dc1cac830797f03/docker-wireguard-tunnel.png -------------------------------------------------------------------------------- /example-tls-fly-io.md: -------------------------------------------------------------------------------- 1 | # Example using Docker WireGuard Tunnel with Fly.io 2 | 3 | [Fly.io](https://fly.io/) is a platform to deploy app servers. Note that this is a paid service and may [charge for dedicated IP addresses](https://fly.io/docs/about/pricing/#anycast-ip-addresses). 4 | 5 | This assumes that you have already setup [Fly.io account](https://fly.io/), have [installed their command line tool](https://fly.io/docs/hands-on/install-flyctl/) and have entered your credit card details on your Fly.io account. 6 | 7 | ## Server 8 | 9 | Will accept connections on behalf of a peer and tunnel them to the designated peer. 10 | 11 | `fly.toml` 12 | 13 | ```yml 14 | # fly.toml app configuration file 15 | # 16 | # See https://fly.io/docs/reference/configuration/ for information about how to use this file. 17 | # 18 | 19 | # Choose your own fly.dev subdomain here 20 | app = "my-app-1234" 21 | 22 | [build] 23 | image = "ghcr.io/digitallyrefined/docker-wireguard-tunnel:v3" 24 | 25 | [env] 26 | DOMAIN = "my-app-1234.fly.dev" # Update this to match your subdomain 27 | PEERS = "1" 28 | SERVICES = "peer1:nginx:80:8080" 29 | 30 | [[mounts]] 31 | source = "wireguard_data" 32 | destination = "/etc/wireguard" 33 | 34 | [[services]] 35 | protocol = "udp" 36 | internal_port = 51820 37 | 38 | [[services.ports]] 39 | port = 51820 40 | 41 | [[services]] 42 | protocol = "tcp" 43 | internal_port = 8080 44 | 45 | [[services.ports]] 46 | port = 443 47 | handlers = ["tls", "http"] 48 | [services.ports.tls_options] 49 | alpn = ["h2", "http/1.1"] 50 | versions = ["TLSv1.2", "TLSv1.3"] 51 | ``` 52 | 53 | ```bash 54 | fly launch 55 | ``` 56 | 57 | Use the following options: 58 | 59 | ```log 60 | ? Would you like to copy its configuration to the new app? Yes 61 | ? Choose an app name (leaving blank will default to 'my-app-1234') change-me 62 | ? Choose a region for deployment: Denver, Colorado (US) (den) # Or a location closest to you 63 | ? Would you like to set up a Postgresql database now? No 64 | ? Would you like to set up an Upstash Redis database now? No 65 | ? Would you like to deploy now? Yes 66 | ? Would you like to allocate a dedicated ipv4 address now? Yes 67 | ``` 68 | 69 | Once started, a `peer1.conf` file will be automatically generated in the `/etc/wireguard` directory, it can be viewed and then removed via: 70 | 71 | ```bash 72 | fly ssh console 73 | cat /etc/wireguard/peer1.conf 74 | # Copy the contents of peer1.conf 75 | rm /etc/wireguard/peer1.conf 76 | ``` 77 | 78 | ## Peer 79 | 80 | Will connect to the server via WireGuard and setup a tunnel to expose the listed ports. 81 | 82 | Paste the `peer1.conf` contents from the Fly.io server into a file named `config/wg0.conf` on the peer. 83 | 84 | `docker-compose.yml` 85 | 86 | ```yml 87 | services: 88 | wireguard-tunnel-peer: 89 | image: ghcr.io/digitallyrefined/docker-wireguard-tunnel:v3 90 | container_name: wireguard-tunnel-peer 91 | environment: 92 | # Note that DOMAIN & PEERS are not required for the peer 93 | # Services to expose format (comma-separated) 94 | # SERVICES=peer-id:peer-container-name:peer-container-port:expose-port-as 95 | - SERVICES=peer1:nginx:80:8080 96 | cap_add: 97 | - NET_ADMIN 98 | volumes: 99 | - ./config:/etc/wireguard 100 | restart: unless-stopped 101 | links: 102 | - nginx:nginx 103 | 104 | nginx: 105 | image: nginx 106 | restart: unless-stopped 107 | ``` 108 | 109 | ```bash 110 | docker compose up -d 111 | docker compose logs -f 112 | ``` 113 | 114 | Once started you should be able to access the demo nginx server via the domain name that was created by Fly.io, for example: 115 | `https://my-app-1234.fly.dev` 116 | -------------------------------------------------------------------------------- /example-tls-traefik.md: -------------------------------------------------------------------------------- 1 | # Example using Docker WireGuard Tunnel with Traefik 2 | 3 | [Traefik](https://traefik.io/) can allow multiple Docker services to be served from a single server using different domain names and can automatically provision TLS/HTTPS certificates via [Let's Encrypt](https://letsencrypt.org/) and a HTTP challenge. 4 | 5 | This assumes that you have already setup subdomain DNS entries for your domain, for example: 6 | `wireguard-tunnel.example.com`, `nginx.example.com` and `nginx-demo.example.com` 7 | 8 | ## Server 9 | 10 | Will accept connections on behalf of a peer and tunnel them to the designated peer. 11 | 12 | `docker-compose.yml` 13 | 14 | ```yml 15 | services: 16 | traefik: 17 | image: "traefik:v2.9" 18 | container_name: "traefik" 19 | restart: unless-stopped 20 | ports: 21 | - "80:80" 22 | - "443:443" 23 | # (Optional) Expose Dashboard 24 | # - "8080:8080" # Don't do this in production! 25 | volumes: 26 | - ./traefik:/etc/traefik 27 | - /var/run/docker.sock:/var/run/docker.sock:ro 28 | networks: 29 | - "traefik" 30 | 31 | wireguard-tunnel-server: 32 | image: ghcr.io/digitallyrefined/docker-wireguard-tunnel:v3 33 | container_name: wireguard-tunnel-server 34 | environment: 35 | # Update to your domain 36 | - DOMAIN=wireguard-tunnel.example.com 37 | # Number of peers to auto generate config for 38 | - PEERS=1 39 | # Services to expose format (comma-separated) 40 | # SERVICES=peer-id:peer-container-name:peer-container-port:expose-port-as 41 | - SERVICES=peer1:nginx:80:8080,peer1:nginx-demo:80:8081 42 | cap_add: 43 | - NET_ADMIN 44 | volumes: 45 | - ./wireguard/config:/etc/wireguard 46 | restart: unless-stopped 47 | ports: 48 | - '51820:51820/udp' 49 | networks: 50 | - "traefik" 51 | labels: 52 | traefik.enable: true 53 | 54 | traefik.http.routers.nginx.entrypoints: web,websecure 55 | traefik.http.routers.nginx.rule: Host(`nginx.example.com`) # Update to your domain 56 | traefik.http.routers.nginx.tls: true 57 | traefik.http.routers.nginx.tls.certresolver: production 58 | traefik.http.services.nginx.loadbalancer.server.port: 8080 59 | 60 | traefik.http.routers.nginx-demo.entrypoints: web,websecure 61 | traefik.http.routers.nginx-demo.rule: Host(`nginx-demo.example.com`) # Update to your domain 62 | traefik.http.routers.nginx-demo.tls: true 63 | traefik.http.routers.nginx-demo.tls.certresolver: production 64 | traefik.http.services.nginx-demo.loadbalancer.server.port: 8081 65 | 66 | networks: 67 | traefik: 68 | external: true 69 | ``` 70 | 71 | `config/traefik.yml` 72 | 73 | ```yml 74 | global: 75 | checkNewVersion: false 76 | sendAnonymousUsage: false # true by default 77 | 78 | # (Optional) Log information 79 | # --- 80 | log: 81 | level: WARNING #ERROR # DEBUG, INFO, WARNING, ERROR, CRITICAL 82 | # format: common # common, json, logfmt 83 | # filePath: /var/log/traefik/traefik.log 84 | 85 | # (Optional) Accesslog 86 | # --- 87 | # accesslog: 88 | # format: common # common, json, logfmt 89 | # filePath: /var/log/traefik/access.log 90 | 91 | # (Optional) Enable API and Dashboard 92 | # --- 93 | api: 94 | dashboard: false # true by default 95 | debug: false 96 | insecure: false # Don't do this in production! 97 | 98 | # Entry Points configuration 99 | # --- 100 | entryPoints: 101 | web: 102 | address: :80 103 | # (Optional) Redirect to HTTPS 104 | # --- 105 | http: 106 | redirections: 107 | entryPoint: 108 | to: websecure 109 | scheme: https 110 | priority: 1000 111 | 112 | websecure: 113 | address: :443 114 | 115 | ping: 116 | entryPoint: web 117 | 118 | certificatesResolvers: 119 | production: 120 | acme: 121 | email: you@example.com # Update this! 122 | storage: /etc/traefik/certs/acme.json 123 | caServer: "https://acme-v02.api.letsencrypt.org/directory" 124 | httpChallenge: 125 | entryPoint: web 126 | 127 | serversTransport: 128 | insecureSkipVerify: true 129 | 130 | providers: 131 | docker: 132 | exposedByDefault: false # Default is true 133 | file: 134 | # watch for dynamic configuration changes 135 | directory: /etc/traefik 136 | watch: true 137 | ``` 138 | 139 | ```bash 140 | docker compose up -d 141 | docker compose logs -f 142 | ``` 143 | 144 | Once started, a `peer1.conf` file will be automatically generated in the `config` directory. 145 | 146 | Next create the certificates storage file with the correct permissions via: 147 | 148 | ```bash 149 | docker compose exec traefik install -D -m 600 /dev/null /etc/traefik/certs/acme.json 150 | docker compose restart 151 | ``` 152 | 153 | ## Peer 154 | 155 | Will connect to the server via WireGuard and setup a tunnel to expose the listed ports. 156 | 157 | Move the `config/peer1.conf` file from the server that was automatically generated and rename it to `config/wg0.conf` on the peer. 158 | 159 | `docker-compose.yml` 160 | 161 | ```yml 162 | services: 163 | wireguard-tunnel-peer: 164 | image: ghcr.io/digitallyrefined/docker-wireguard-tunnel:v3 165 | container_name: wireguard-tunnel-peer 166 | environment: 167 | # Note that DOMAIN & PEERS are not required for the peer 168 | # Services to expose format (comma-separated) 169 | # SERVICES=peer-id:peer-container-name:peer-container-port:expose-port-as 170 | - SERVICES=peer1:nginx:80:8080,peer1:nginx-demo:80:8081 171 | cap_add: 172 | - NET_ADMIN 173 | volumes: 174 | - ./config:/etc/wireguard 175 | restart: unless-stopped 176 | links: 177 | - nginx:nginx 178 | - nginx-demo:nginx-demo 179 | 180 | nginx: 181 | image: nginx 182 | restart: unless-stopped 183 | 184 | nginx-demo: 185 | image: nginxdemos/hello 186 | restart: unless-stopped 187 | ``` 188 | 189 | ```bash 190 | docker compose up -d 191 | docker compose logs -f 192 | ``` 193 | 194 | Note: if you have a firewall in front of your server you will need to allow connections on port `51820/udp` for the WireGuard server, and connections on ports `80` and `443` for Traefik. 195 | 196 | Once started you should be able to access both nginx servers via the domain names listed on the WireGuard server, for example: 197 | `https://nginx.example.com` and `https://nginx-demo.example.com` 198 | -------------------------------------------------------------------------------- /wg-start.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | wireguard_port="${WIREGUARD_PORT:-51820}" 4 | wireguard_mtu="${WIREGUARD_MTU:-1280}" 5 | 6 | if [ ! -f /etc/wireguard/wg0.conf ]; then 7 | server_private="$(wg genkey)" 8 | server_public=$(echo -n "${server_private}" | wg pubkey) 9 | 10 | cat >/etc/wireguard/wg0.conf </etc/wireguard/peer$peer_number.conf <>/etc/wireguard/wg0.conf <>/etc/rinetd.conf 61 | echo "0.0.0.0 $expose_port_as/udp 10.0.0.$peer_number $expose_port_as/udp" >>/etc/rinetd.conf 62 | else 63 | echo "0.0.0.0 $expose_port_as $service_hostname $container_port" >>/etc/rinetd.conf 64 | echo "0.0.0.0 $expose_port_as/udp $service_hostname $container_port/udp" >>/etc/rinetd.conf 65 | fi 66 | done 67 | 68 | echo "$(date): Starting Internet redirection server" 69 | rinetd 70 | 71 | echo "$(date): Starting Wireguard" 72 | wg-quick up wg0 73 | 74 | finish() { 75 | echo "$(date): Shutting down Wireguard" 76 | timeout 5 wg-quick down wg0 77 | 78 | exit 0 79 | } 80 | 81 | trap finish TERM INT QUIT 82 | 83 | wg 84 | 85 | while :; do 86 | if [ $(timeout 5 wg | wc -l) == 0 ]; then 87 | exit 1 88 | fi 89 | sleep 10 90 | done 91 | --------------------------------------------------------------------------------