<p>We are deploying a Java backend and React UI application using docker-compose.  Our Docker containers are running Java, Caddy, and Postgres.</p>
<p>What's unusual about this architecture is that we are not running the application as a cluster.  Each user gets their own server with their own subdomain.  Everything is working nicely, but we need a strategy for managing/updating machines as the number of users grows.</p>
<p>We can accept some down time in the middle of the night, so we don't need to have high availability.</p>
<p>We're just not sure what would be the best way to update software on all machines.  And we are pretty new to Docker and have no experience with Kubernetes or Ansible, Chef, Puppet, etc.  But we are quick to pick things up.</p>
<p>We expect to have hundreds to thousands of users.  Each machine runs the same code but has environment variables that are unique to the user.  Our original provisioning takes care of that, so we do not anticipate having to change those with software updates.  But a solution that can also provide that ability would not be a bad thing.</p>
<p>So, the question is, when we make code changes and want to deploy the updated Java jar or the React application, what would be the best way to get those out there in an automated fashion?</p>
<p>Some things we have considered:</p>
<ul>
<li>Docker Hub (concerns about rate limiting)</li>
<li>Deploying our own Docker repo</li>
<li>Kubernetes</li>
<li>Ansible</li>
<li><a href="https://containrrr.dev/watchtower/" rel="nofollow noreferrer">https://containrrr.dev/watchtower/</a></li>
</ul>
<p>Other things that we probably need include GitHub actions to build and update the Docker images.</p>
<p>We are open to ideas that are not listed here, because there is a lot we don't know about managing many machines running docker-compose.  So please feel free to offer suggestions.  Many thanks!</p>

<p>In your case I advice you to use <a href="https://kubernetes.io/" rel="nofollow noreferrer">Kubernetes</a> combination with CD tools. One of it is <a href="https://buddy.works/" rel="nofollow noreferrer">Buddy</a>. I think it is the best way to make such updates in an automated fashion. Of course you can use just Kubernetes, but with Buddy or other CD tools you will make it faster and easier.  In my answer I am describing Buddy but there are a lot of popular CD tools for automating workflows in Kubernetes like for example: <a href="https://docs.gitlab.com/ee/topics/autodevops/" rel="nofollow noreferrer">GitLab</a> or <a href="https://codefresh.io/kubernetes-deploy/" rel="nofollow noreferrer">CodeFresh.io</a> - you should pick which one is actually best for you. Take a look: <a href="https://medium.com/@OPTASY.com/what-are-the-best-continuous-deployment-tools-for-kubernetes-and-why-top-5-c5f42e44983d" rel="nofollow noreferrer">CD-automation-tools-Kubernetes</a>.</p>
<p>With Buddy you can avoid most of these steps while automating updates - (executing <code>kubectl apply,</code> <code>kubectl set image</code> commands ) by doing a simple push to Git.</p>
<p>Every time you updates your application code or Kubernetes configuration, you have two possibilities to update your cluster:  <code>kubectl apply</code>  or  <code>kubectl set image</code>.</p>
<p>Such workflow most often looks like:</p>
<p><strong>1.</strong>  Edit application code or configuration .YML file</p>
<p><strong>2.</strong>  Push changes to your Git repository</p>
<p><strong>3.</strong>  Build an new Docker image</p>
<p><strong>4.</strong>  Push the Docker image</p>
<p><strong>5.</strong>  Log in to your K8s cluster</p>
<p><strong>6.</strong>  Run  <code>kubectl apply</code>  or  <code>kubectl set image</code> commands to apply changes into K8s cluster</p>
<p>Buddy is a CD tool that you can use to automate your whole K8s release workflows like:</p>
<ul>
<li>managing Dockerfile updates</li>
<li>building Docker images and pushing them to the Docker registry</li>
<li>applying new images on your K8s cluster</li>
<li>managing configuration changes of a K8s Deployment
etc.</li>
</ul>
<p>With Buddy you will have to configure just one pipeline.</p>
<p>With every change in  your app code or the YAML config file, this tool will apply the deployment and Kubernetes will start transforming the containers to the desired state.</p>
Pipeline configuration for running Kubernetes pods or jobs
<p>Assume that we have application on a K8s cluster and the its repository contains:</p>
<ul>
<li>source code of our application</li>
<li>a Dockerfile with instructions on creating an image of your app</li>
<li>DB migration scripts</li>
<li>a Dockerfile with instructions on creating an image that will run the migration during the deployment (db migration runner)</li>
</ul>
<p>In this case, we can configure a pipeline that will:</p>
<p><strong>1.</strong> Build application and migrate images</p>
<p><strong>2.</strong> Push them to the Docker Hub</p>
<p><strong>3.</strong> Trigger the DB migration using the previously built image. We can define the image, commands and deployment and use YAML file.</p>
<p><strong>4.</strong>  Use either Apply K8s Deployment or Set K8s Image to update the image in your K8s application.</p>
<p>You can adjust above workflow properly to your environment/applications properties.</p>
<p>Buddy supports GitLab as a Git provider. Integration of these two tools  is easy and only requires authorizing GitLab in your profile. Thanks to this integration you can create pipelines that will build, test and deploy your app code to the server. But of course if you are using GitLab there is no need to set up Buddy as an extra tool because GitLab is also CD tools tool for automating workflows in Kubernetes.
More information you can find here: <a href="https://buddy.works/guides/how-optimize-kubernetes-workflow" rel="nofollow noreferrer">buddy-workflow-kubernetes</a>.</p>
<p>Read also: <a href="https://enterprisersproject.com/article/2020/7/kubernetes-workflows-and-processes-can-automate" rel="nofollow noreferrer">automating-workflows-kubernetes</a>.</p>
