{"id":34787889,"url":"https://github.com/serversideup/github-action-docker-swarm-deploy","last_synced_at":"2025-12-25T09:33:36.522Z","repository":{"id":73851144,"uuid":"599163269","full_name":"serversideup/github-action-docker-swarm-deploy","owner":"serversideup","description":"Deploy to Docker Swarm clusters from GitHub Actions with ease.","archived":false,"fork":false,"pushed_at":"2025-11-07T16:43:23.000Z","size":316,"stargazers_count":27,"open_issues_count":0,"forks_count":6,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-11-07T18:25:48.454Z","etag":null,"topics":["docker","docker-swarm","github-actions","swarm"],"latest_commit_sha":null,"homepage":"https://serversideup.net/open-source/spin/","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/serversideup.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"serversideup"}},"created_at":"2023-02-08T15:29:29.000Z","updated_at":"2025-11-07T16:39:41.000Z","dependencies_parsed_at":"2023-09-24T17:56:25.075Z","dependency_job_id":"ffc020cc-35ee-4503-a815-6bfa095856b7","html_url":"https://github.com/serversideup/github-action-docker-swarm-deploy","commit_stats":{"total_commits":22,"total_committers":2,"mean_commits":11.0,"dds":"0.045454545454545414","last_synced_commit":"b9d361fa7a8bb6cd06f1454425f4a415e6f72ca7"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/serversideup/github-action-docker-swarm-deploy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/serversideup%2Fgithub-action-docker-swarm-deploy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/serversideup%2Fgithub-action-docker-swarm-deploy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/serversideup%2Fgithub-action-docker-swarm-deploy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/serversideup%2Fgithub-action-docker-swarm-deploy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/serversideup","download_url":"https://codeload.github.com/serversideup/github-action-docker-swarm-deploy/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/serversideup%2Fgithub-action-docker-swarm-deploy/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28025628,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-25T02:00:05.988Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","docker-swarm","github-actions","swarm"],"created_at":"2025-12-25T09:33:35.444Z","updated_at":"2025-12-25T09:33:36.508Z","avatar_url":"https://github.com/serversideup.png","language":null,"funding_links":["https://github.com/sponsors/serversideup"],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\t\t\u003cimg src=\".github/img/readme-header.png\" width=\"1280\" alt=\"Header Image\"\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n\t\u003ca href=\"https://github.com/serversideup/github-action-docker-swarm-deploy/blob/main/LICENSE\" target=\"_blank\"\u003e\u003cimg src=\"https://badgen.net/github/license/serversideup/github-action-docker-swarm-deploy\" alt=\"License\"\u003e\u003c/a\u003e\n\t\u003ca href=\"https://github.com/sponsors/serversideup\"\u003e\u003cimg src=\"https://badgen.net/badge/icon/Support%20Us?label=GitHub%20Sponsors\u0026color=orange\" alt=\"Support us\"\u003e\u003c/a\u003e\n  \u003cbr /\u003e\n  \u003ca href=\"https://community.serversideup.net\"\u003e\u003cimg alt=\"Discourse users\" src=\"https://img.shields.io/discourse/users?color=blue\u0026server=https%3A%2F%2Fcommunity.serversideup.net\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://serversideup.net/discord\"\u003e\u003cimg alt=\"Discord\" src=\"https://img.shields.io/discord/910287105714954251?color=blueviolet\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n## Introduction\nThis is a GitHub Action intended to simplify the deployment experience with GitHub Actions + Docker Swarm.\n\n### Features:\n- 😃 Simple to use\n- 🏗️ Bring your own container + configuration\n- 💯 Replicate 100% of production from Development to CI to Deployment\n- 💪 Use with self-hosted registries\n- 🧮 Store MD5 hashes in environment variables for deployment\n- 🔐 Use with private registries\n- 🏠 Use .env files for deployment\n\n## Usage\nHere is an example workflow:\n\n```yml\nname: Production Deployment\non:\n  push:\n    branches:\n      - main\n\njobs:\n  deploy:\n    needs: build\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: serversideup/github-action-docker-swarm-deploy@v3\n        with:\n          ssh_deploy_private_key: \"${{ secrets.SSH_DEPLOY_PRIVATE_KEY }}\"\n          ssh_remote_hostname: \"${{ secrets.SSH_REMOTE_HOSTNAME }}\"\n          registry: \"ghcr.io\"\n          registry-username: \"${{ github.actor }}\"\n          registry-password: \"${{ secrets.GITHUB_TOKEN }}\"\n          stack_name: \"${{ env.PROJECT_NAME }}\"\n          md5_file_path: \"./.infrastructure/conf/traefik/prod/traefik.yml\"\n          md5_variable_name: \"SPIN_MD5_HASH_TRAEFIK_YML\"\n          env_file_base64: \"${{ secrets.ENV_FILE_BASE64 }}\"\n```\n## How to use the action\n\nThe following inputs are available:\n\n| Parameter               | Description                                                                                     | Default                                              | Required |\n|-------------------------|--------------------------------------------------------------------------------------------------|------------------------------------------------------|----------|\n| docker_compose_file_path| Set your docker compose file path with the CLI options.                                          | `-c docker-compose.yml -c docker-compose.prod.yml`   | false    |\n| env_file_base64         | The base64 encoded .env file to load into the container.                                         |                                                      | false    |\n| log_level               | The log level to use for the Docker CLI.                                                         | `debug`                                              | false    |\n| md5_file_path           | Set the path to the file you would like to get the MD5 checksum for.                             |                                                      | false    |\n| md5_variable_name       | Set the name of the variable to store the MD5 checksum in.                                       | `MD5_CHECKSUM`                                       | false    |\n| registry                | Comma-separated list of container registries to authenticate with (e.g., \"docker.io,ghcr.io\").   | `docker.io`                                          | false    |\n| registry-password       | The password to use to authenticate with the container registry.                                 |                                                      | ⚠️ true  |\n| registry-username       | The username to use to authenticate with the container registry.                                 |                                                      | ⚠️ true  |\n| ~~registry-token~~      | ⚠️ **DEPRECATED:** Use `registry-password` instead.                                              |                                                      | false    |\n| ssh_deploy_private_key  | The private key you have authenticated to connect to your server via SSH.                        |                                                      | ⚠️ true  |\n| ssh_deploy_user         | The user that you would like to connect as on the remote server via SSH.                         | `deploy`                                             | ⚠️ true  |\n| ssh_remote_hostname     | The hostname or IP address of the server you want to connect to.                                 |                                                      | ⚠️ true  |\n| ssh_remote_known_hosts  | The public key of your SSH server to validate we are connecting to the right server.             |                                                      | false    |\n| ssh_remote_port         | The SSH port of the remote server you would like to connect to.                                  | `22`                                                 | false    |\n| stack_name              | The name of your Docker stack.                                                                   |                                                      | ⚠️ true  |\n\n## Working with SSH\nSSH can have a few moving parts and it's important you get this right. Here's a few pointers to ensure you have the right setup.\n\n### ssh_deploy_user\nThis is the user you want to connect to your server with. This is most likely going to be `deploy` but could be different depending on your setup. It is very important that whatever user you choose, this user should have permissions to run `docker stack deploy` (without `sudo`).\n\n### ssh_remote_hostname\nThis is the hostname or IP address of your server. This is most likely going to be your server's public IP address. This can be `1.2.3.4` or `myserver.example.com`.\n\n### ssh_remote_port\nThis is the port of your SSH server. This is most likely going to be `22` but could be different depending on your setup. Make sure this port is accessible from GitHub Actions. You may have to allow this port through your router, firewall, or security policy with your hosting provider.\n\n### ssh_deploy_private_key\nThis is the private key you use to authenticate to your server via SSH. It must be in a valid private key format. \n\nTo generate a keypair, you can use the following commands:\n\n```bash\nssh-keygen -o -a 100 -t ed25519 -f ~/Desktop/id_ed25519_deploy -C deploy\n```\nThis will create two files on your desktop. You can use `cat` to get the content of your files.\n\n\u003e [!WARNING]  \n\u003e Be sure you're not copying hidden characters or extra whitespaces\n\n```bash\ncat ~/Desktop/id_ed25519_deploy # Get the content of your PRIVATE key\ncat ~/Desktop/id_ed25519_deploy.pub # Get the content of your PUBLIC key\n\n## If you use macOS, you can use `pbcopy` to copy your key to your clipboard\n\ncat ~/Desktop/id_ed25519_deploy | pbcopy # Copy your private key to your clipboard\ncat ~/Desktop/id_ed25519_deploy.pub | pbcopy # Copy your public key to your clipboard\n```\n\n\u003e [!CAUTION]\n\u003e In order for you to connect to your server, the user you're connecting as must have your public key in their **authorized_keys** file.\n\nCopy the output and add it to the `~/.ssh/authorized_keys` file on your server for the user you're connecting as.\n\n### ssh_remote_known_hosts\nThis is the public key of your SSH server to validate we are connecting to the right server. It must be in a [valid known_hosts format](https://www.ibm.com/docs/en/zos/3.1.0?topic=daemon-ssh-known-hosts-file-format).\n\n### Removing the \"ssh_remote_known_hosts\" warning\n![image](.github/img/known-hosts-warning.png)\nFor simplicity sake, we will automatically scan the known public SSH keys of your server and attempt to make a connection. The problem with this is it opens you up to a man-in-the-middle attack.\n\nTo ensure you're validating the identity of your server, you can set the `ssh_remote_known_hosts` input with the public key of your server. You can set this value to a GitHub secret like `SSH_REMOTE_KNOWN_HOSTS`:\n\n```yml\n- uses: serversideup/github-action-docker-swarm-deploy@v3\n  with:\n    registry-password: \"${{ secrets.GITHUB_TOKEN }}\"\n    registry-username: \"${{ github.actor }}\"\n    ssh_deploy_private_key: \"${{ secrets.SSH_DEPLOY_PRIVATE_KEY }}\"\n    ssh_remote_hostname: \"${{ secrets.SSH_REMOTE_HOSTNAME }}\"\n    ssh_remote_known_hosts: \"${{ secrets.SSH_REMOTE_KNOWN_HOSTS }}\" # Set \"SSH_REMOTE_KNOWN_HOSTS\" in GitHub Actions Secrets \n    stack_name: \"${{ env.PROJECT_NAME }}\"\n  env:\n    SPIN_IMAGE_DOCKERFILE_PHP: \"ghcr.io/${{ github.repository }}:${{ github.sha }}\"\n    SPIN_DEPLOYMENT_ENVIRONMENT: production\n```\n#### Setting the \"ssh_remote_known_hosts\" secret\n![image](.github/img/secrets.png)\n\nYou can set the `ssh_remote_known_hosts` secret by getting the public key of your server and setting it as a GitHub secret. You can run this from your **local machine** (not in GitHub Actions) to get the public key of your server:\n\n\u003e [!NOTE]  \n\u003e Replace `myserver.example.com` with the hostname of your server. You can also change the port by changing `-p 22` to your desired port.\n\n```bash\nssh-keyscan -p 22 -H myserver.example.com 2\u003e/dev/null | sort -u\n```\n\nThe output will look similar to this, with hashed hostnames:\n\n```\n|1|BfvcToAPMeAK0zR9FShCnP5CCaw=|ltUazkjjoIKsoBFQMF5yOTJt/Ks= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBKIAz4U9GvgyBttgCnvi4AfBq3CdQ9XqAryrIyO1O60\n|1|J/BpMKspk0BwPAxR28Dzc7gVGgw=|RAimV4/7iS4jlmFmDAfex/nKDUQ= ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQChvNZNpcjdSXJWVdnhieQXRgBVUUwpexLz0dbDegUj68vrzXsgtrnGtf+sJlRhI6C7jBZDfxk2jXL1ASfxEQUqbvptZTG68uusD1DYx3wtb/kTqvJ3JkFuWJbt2zLyZktPrueHA9cvuquW46M6wSZN5AZddNitUZ09Bpb+dTVZkjbEDOiGoHRDj5M86e1rr/8UGNrAVZl/hckup3lfu3B3P0LKnGnMw+/DXIKvJiwVJ3OdHzyq6D/x9uNgcOUA7UPgUbV30gyFtWr2Az6Vn/ZolDOGasK9iI5WjvBdXwyWNwEnnR539RutiwbS/XTnb0Jj/fFS5NM2/AM3nCT37D4uQA7aJFka7keUTJZJIVanziz9Ty76lloweLDKHN2CyvUijjSx5HaqV9Dr2nTefTPPvzz1D9xU0WJX8KC77Wcu8qEjqSwNihJqucXQvq4xeBZ85OGPbvzAFYqZdjynzVsLP50E7kmdaW3VJx88hbg+vyXrJD1urcOVPNtGoMpN2Mc=\n|1|cYUT42KbDx+rQD0YSpKgDxbFWBc=|D13n4gWdMyJ+C2nifoEEeFmezmE= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBN7wgADkhTHi7WER2pCZ5/10HBmhSIAq9zS1rWiBG5A8t2ATh5QnJ17XtPKXEJGPH8nogry/bZ+WKxI4zojGD+Q=\n```\n\nCopy the output and set it as a GitHub secret (usually called `SSH_REMOTE_KNOWN_HOSTS`).\n\n![image](.github/img/secrets.png)\n\n#### Validate the known hosts file\nIf you need to validate the known hosts file, you can save it in a file on your local machine and attempt to SSH into your server with it:\n\n```bash\nssh -p 22 -i /path/to/test_known_hosts_file myserver.example.com\n```\n\nIf you cannot connect from your local machine, then you know there is an issue with the known hosts file itself.\n\n## Advanced Usage\nWe also have some helpful features for our power users out there.\n\n### Getting the MD5 Checksum of a file\nWe include an optional input to get the MD5 checksum of a file. This is useful if you're working with Docker Configs and you only want the service to update if the file has changed. You just need to set the following inputs (a full example is available at the top of this document):\n\n```yml\nsteps:\n  - uses: serversideup/github-action-docker-swarm-deploy@v3\n    with:\n      md5_file_path: \"./path/to/my/file.txt\"\n      md5_variable_name: \"MY_FILE_MD5\"\n```\n\nThis will store the MD5 checksum of the file at `./path/to/my/file.txt` in the environment variable `MY_FILE_MD5`.\n\n### Using an .env file\nYou can use an .env file to set environment variables for your container. This is useful if you're working with environment variables that are different for each environment. You can set the .env file as a base64 encoded string and it will be decoded and loaded into the container.\n\n```yml\nsteps:\n  - uses: serversideup/github-action-docker-swarm-deploy@v3\n    with:\n      env_file_base64: \"${{ secrets.ENV_FILE_BASE64 }}\"\n```\n\nTo set the value of `ENV_FILE_BASE64`, you can use the following command:\n\n```bash\ncat .env | base64\n```\n\nAny variable set in the .env file will be available to the deployment to be used in your docker-compose.yml file.\n\nFor example, if you have a .env file with the following:\n\n```\nDB_HOST=mysql\nDB_PORT=3306\nDB_USER=root\nDB_PASSWORD=password\n```\n\nThen you can use the following in your docker-compose.yml file: \n\n```yml\nservices:\n  mysql:\n    environment:\n      - DB_HOST=${{ env.DB_HOST }}\n      - DB_PORT=${{ env.DB_PORT }}\n      - DB_USER=${{ env.DB_USER }}\n      - DB_PASSWORD=${{ env.DB_PASSWORD }}\n```\n\n## Resources\n- **[Discord](https://serversideup.net/discord)** for friendly support from the community and the team.\n- **[GitHub](https://github.com/serversideup/github-action-docker-swarm-deploy)** for source code, bug reports, and project management.\n- **[Get Professional Help](https://serversideup.net/professional-support)** - Get video + screen-sharing help directly from the core contributors.\n\n## Contributing\nAs an open-source project, we strive for transparency and collaboration in our development process. We greatly appreciate any contributions members of our community can provide. Whether you're fixing bugs, proposing features, improving documentation, or spreading awareness - your involvement strengthens the project. Please review our [contribution guidelines](https://serversideup.net/open-source/github-action-docker-swarm-deploy/docs/getting-started/contributing) and [code of conduct](./.github/code_of_conduct.md) to understand how we work together respectfully.\n\n- **Bug Report**: If you're experiencing an issue while using this action, please [create an issue](https://github.com/serversideup/github-action-docker-swarm-deploy/issues/new/choose).\n- **Feature Request**: Make this project better by [submitting a feature request](https://github.com/serversideup/github-action-docker-swarm-deploy/issues/new/choose).\n- **Documentation**: Improve our documentation by [submitting a documentation change](./README.md).\n- **Community Support**: Help others on [Discord](https://serversideup.net/discord).\n- **Security Report**: Report critical security issues via [our responsible disclosure policy](https://www.notion.so/Responsible-Disclosure-Policy-421a6a3be1714d388ebbadba7eebbdc8).\n\nNeed help getting started? Join our Discord community and we'll help you out!\n\n\u003ca href=\"https://serversideup.net/discord\"\u003e\u003cimg src=\"https://serversideup.net/wp-content/themes/serversideup/images/open-source/join-discord.svg\" title=\"Join Discord\"\u003e\u003c/a\u003e\n\n## Our Sponsors\nAll of our software is free an open to the world. None of this can be brought to you without the financial backing of our sponsors.\n\n\u003cp align=\"center\"\u003e\u003ca href=\"https://github.com/sponsors/serversideup\"\u003e\u003cimg src=\"https://521public.s3.amazonaws.com/serversideup/sponsors/sponsor-box.png\" alt=\"Sponsors\"\u003e\u003c/a\u003e\u003c/p\u003e\n\n#### Bronze Sponsors\n\u003c!-- bronze --\u003eNo bronze sponsors yet. \u003ca href=\"https://github.com/sponsors/serversideup\"\u003eBecome a sponsor →\u003c/a\u003e\u003c!-- bronze --\u003e\n\n#### Individual Supporters\n\u003c!-- supporters --\u003e\u003ca href=\"https://github.com/GeekDougle\"\u003e\u003cimg src=\"https://github.com/GeekDougle.png\" width=\"40px\" alt=\"GeekDougle\" /\u003e\u003c/a\u003e\u0026nbsp;\u0026nbsp;\u003ca href=\"https://github.com/JQuilty\"\u003e\u003cimg src=\"https://github.com/JQuilty.png\" width=\"40px\" alt=\"JQuilty\" /\u003e\u003c/a\u003e\u0026nbsp;\u0026nbsp;\u003c!-- supporters --\u003e\n\n## About Us\nWe're [Dan](https://twitter.com/danpastori) and [Jay](https://twitter.com/jaydrogers) - a two person team with a passion for open source products. We created [Server Side Up](https://serversideup.net) to help share what we learn.\n\n\u003cdiv align=\"center\"\u003e\n\n| \u003cdiv align=\"center\"\u003eDan Pastori\u003c/div\u003e                  | \u003cdiv align=\"center\"\u003eJay Rogers\u003c/div\u003e                                 |\n| ----------------------------- | ------------------------------------------ |\n| \u003cdiv align=\"center\"\u003e\u003ca href=\"https://twitter.com/danpastori\"\u003e\u003cimg src=\"https://serversideup.net/wp-content/uploads/2023/08/dan.jpg\" title=\"Dan Pastori\" width=\"150px\"\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://twitter.com/danpastori\"\u003e\u003cimg src=\"https://serversideup.net/wp-content/themes/serversideup/images/open-source/twitter.svg\" title=\"Twitter\" width=\"24px\"\u003e\u003c/a\u003e\u003ca href=\"https://github.com/danpastori\"\u003e\u003cimg src=\"https://serversideup.net/wp-content/themes/serversideup/images/open-source/github.svg\" title=\"GitHub\" width=\"24px\"\u003e\u003c/a\u003e\u003c/div\u003e                        | \u003cdiv align=\"center\"\u003e\u003ca href=\"https://twitter.com/jaydrogers\"\u003e\u003cimg src=\"https://serversideup.net/wp-content/uploads/2023/08/jay.jpg\" title=\"Jay Rogers\" width=\"150px\"\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://twitter.com/jaydrogers\"\u003e\u003cimg src=\"https://serversideup.net/wp-content/themes/serversideup/images/open-source/twitter.svg\" title=\"Twitter\" width=\"24px\"\u003e\u003c/a\u003e\u003ca href=\"https://github.com/jaydrogers\"\u003e\u003cimg src=\"https://serversideup.net/wp-content/themes/serversideup/images/open-source/github.svg\" title=\"GitHub\" width=\"24px\"\u003e\u003c/a\u003e\u003c/div\u003e                                       |\n\n\u003c/div\u003e\n\n### Find us at:\n\n* **📖 [Blog](https://serversideup.net)** - Get the latest guides and free courses on all things web/mobile development.\n* **🙋 [Community](https://community.serversideup.net)** - Get friendly help from our community members.\n* **🤵‍♂️ [Get Professional Help](https://serversideup.net/professional-support)** - Get video + screen-sharing support from the core contributors.\n* **💻 [GitHub](https://github.com/serversideup)** - Check out our other open source projects.\n* **📫 [Newsletter](https://serversideup.net/subscribe)** - Skip the algorithms and get quality content right to your inbox.\n* **🐥 [Twitter](https://twitter.com/serversideup)** - You can also follow [Dan](https://twitter.com/danpastori) and [Jay](https://twitter.com/jaydrogers).\n* **❤️ [Sponsor Us](https://github.com/sponsors/serversideup)** - Please consider sponsoring us so we can create more helpful resources.\n\n## Our products\nIf you appreciate this project, be sure to check out our other projects.\n\n### ⚡️ Starter Kits\n- **[Spin Pro](https://getspin.pro)**: Laravel Sail alternative for running Docker from development → production.\n\n### 📚 Books\n- **[The Ultimate Guide to Building APIs \u0026 SPAs](https://serversideup.net/ultimate-guide-to-building-apis-and-spas-with-laravel-and-nuxt3/)**: Build web \u0026 mobile apps from the same codebase.\n- **[Building Multi-Platform Browser Extensions](https://serversideup.net/building-multi-platform-browser-extensions/)**: Ship extensions to all browsers from the same codebase.\n\n### 🛠️ Software-as-a-Service\n- **[Bugflow](https://bugflow.io/)**: Get visual bug reports directly in GitHub, GitLab, and more.\n- **[SelfHost Pro](https://selfhostpro.com/)**: Connect Stripe or Lemonsqueezy to a private docker registry for self-hosted apps.\n\n### 🌍 Open Source\n- **[AmplitudeJS](https://521dimensions.com/open-source/amplitudejs)**: Open-source HTML5 \u0026 JavaScript Web Audio Library.\n- **[Spin](https://serversideup.net/open-source/spin/)**: Laravel Sail alternative for running Docker from development → production.\n- **[Financial Freedom](https://github.com/serversideup/financial-freedom)**: Open source alternative to Mint, YNAB, \u0026 Monarch Money.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fserversideup%2Fgithub-action-docker-swarm-deploy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fserversideup%2Fgithub-action-docker-swarm-deploy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fserversideup%2Fgithub-action-docker-swarm-deploy/lists"}