Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Self Hosting: Docker Compose setup and flow #640

Merged
merged 4 commits into from
May 6, 2024
Merged

Self Hosting: Docker Compose setup and flow #640

merged 4 commits into from
May 6, 2024

Conversation

Radu-C-Martin
Copy link
Contributor

My attempt at implementing Issue #627.

I'm for sure missing some stuff, so please don't hesitate to mention it.

The force_ssl environment configs are based on this discussion.

@zachgoll
Copy link
Collaborator

zachgoll commented Apr 18, 2024

@Radu-C-Martin nice start here!

I think the goal for this issue will be to merge an "all in one" solution that is well tested and validated across the entire self-hosting flow, which will include:

Any thoughts on how we can test and validate amongst the community? I'd imagine we'll have quite a few different types of setups to run through.

@zachgoll
Copy link
Collaborator

I'm going to mark this pull request as a draft and would encourage any other self-hosters to collaborate on this branch to get this to completion. Would love to hear more thoughts around:

  • The best way to test and collaborate on a flow like this
  • What self-hosted Docker flows do we need to prioritize and support? (i.e. fresh VPS setup, integrating with HomeLab setups, etc.)
  • What is our long-term method for ensuring this flow stays up-to-date as the app evolves

@zachgoll zachgoll changed the title feat: add docker compose file and publish workflow Self Hosting: Docker Compose setup and flow Apr 18, 2024
@zachgoll zachgoll marked this pull request as draft April 18, 2024 18:40
@zachgoll zachgoll added the 💻 Self Hosted only Issues pertaining to self-hosted versions of Maybe label Apr 18, 2024
@Radu-C-Martin
Copy link
Contributor Author

I played around with this some more.

I adjusted the deployment workflow to tag not only the complete version, but also a major.minor tag and a major-only tag (this probably would only make sense if breaking changes only happen in major versions?).

I also added a step to prune the untagged images when deploying, since creating a new image for every commit will generate quite a few images :)

Additionally, I updated the documentation a bit to reflect the docker deployment. From commercial VPS I could only test Linode, but it works well there.

I left the references to my images in the docker-compose file for now, so that people can play around with that and suggest changes.

One thing to note is that the latest tag follows the last image that commit that was tagged, so for example if you first tag a v.2.1.2, and then a fix for v.1.5.6 or something, the latest tag will be on the v.1.5.6. But in that case we would probably need more logic in the workflow anyway, so tell me if that's something you want me to consider :)

@zachgoll
Copy link
Collaborator

Coming along nicely! Would love to hear from additional self-hosters on their own use-cases and review of this flow.

@DorianMazur
Copy link

DorianMazur commented Apr 24, 2024

I just tested this docker compose and it's not working for me :/

ActiveRecord::ConnectionNotEstablished: connection to server at "172.29.0.2", port 5432 failed: fe_sendauth: no password supplied (ActiveRecord::ConnectionNotEstablished)
Caused by:
PG::ConnectionBad: connection to server at "172.29.0.2", port 5432 failed: fe_sendauth: no password supplied (PG::ConnectionBad)
Tasks: TOP => db:prepare

EDIT:
It's working now, I removed special characters from password. It seems that they are not supported.

Copy link

@gantoine gantoine left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good, can't wait to see this merged! just a couples questions/comments

@Radu-C-Martin Radu-C-Martin marked this pull request as ready for review April 30, 2024 08:24
@maddox
Copy link

maddox commented Apr 30, 2024

Am I missing something, or is there not really a docker image actually available yet?

ERROR: Head "https://ghcr.io/v2/maybe-finance/maybe/manifests/latest": denied

@Radu-C-Martin
Copy link
Contributor Author

Radu-C-Martin commented Apr 30, 2024

Am I missing something, or is there not really a docker image actually available yet?

ERROR: Head "https://ghcr.io/v2/maybe-finance/maybe/manifests/latest": denied

Not available yet, it will be available once this gets merged. This PR also includes the publish workflow 😅

If you want to check if everything works, you could use the ghcr.io/radu-c-martin/maybe image (cf. my fork).

@maddox
Copy link

maddox commented Apr 30, 2024

Gotcha, thanks!

@zachgoll
Copy link
Collaborator

@Radu-C-Martin I'll do some local testing on all of this today/tomorrow and we'll get it merged!

README.md Outdated
@@ -30,6 +30,10 @@ You can find [detailed setup guides for self hosting here](docs/self-hosting.md)
1. Click the button above
2. Follow the instructions in the [Render self-hosting guide](docs/self-hosting/render.md)

### Manual docker deploy

A docker image, along with an example docker-compose file are provided for those who wish to deploy the application that way.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
A docker image, along with an example docker-compose file are provided for those who wish to deploy the application that way.
To host Maybe with Docker Compose, please follow our [Docker self-hosting guide](docs/self-hosting/docker.md).

README.md Outdated
@@ -30,6 +30,10 @@ You can find [detailed setup guides for self hosting here](docs/self-hosting.md)
1. Click the button above
2. Follow the instructions in the [Render self-hosting guide](docs/self-hosting/render.md)

### Manual docker deploy
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### Manual docker deploy
### Docker

- .env
environment:
DB_HOST: "postgres"
HOSTING_PLATFORM: "localhost"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can remove HOSTING_PLATFORM. The original reason for introducing this variable was in relation to auto-upgrades, but those assumptions are no longer relevant based on the iterations we've gone through here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to this, we will need to hide both "auto updates" and "provider settings" in the self hosted settings since these do not apply to an app running via docker compose:

<%= settings_section title: t(".general_settings_title") do %>
<%= form_with model: Setting.new, url: settings_hosting_path, method: :patch, local: true, html: { class: "space-y-6", data: { controller: "auto-submit-form", "auto-submit-form-trigger-event-value" => "blur" } } do |form| %>
<div>
<h2 class="font-medium mb-1"><%= t(".upgrades.title") %></h2>
<p class="text-gray-500 text-sm mb-4"><%= t(".upgrades.description") %></p>
<div class="space-y-4">
<div class="flex items-center gap-4">
<%= form.radio_button :upgrades_mode, "manual", checked: Setting.upgrades_mode == "manual", data: { "auto-submit-form-target" => "auto", "autosubmit-trigger-event": "input" } %>
<%= form.label :upgrades_mode_manual, t(".upgrades.manual.title"), class: "text-gray-900 text-sm" do %>
<span class="font-medium"><%= t(".upgrades.manual.title") %></span>
<br>
<span class="text-gray-500">
<%= t(".upgrades.manual.description") %>
</span>
<% end %>
</div>
<div class="flex items-center gap-4">
<%= form.radio_button :upgrades_mode, "release", checked: Setting.upgrades_mode == "auto" && Setting.upgrades_target == "release", data: { "auto-submit-form-target" => "auto", "autosubmit-trigger-event": "input" } %>
<%= form.label :upgrades_mode_release, t(".upgrades.latest_release.title"), class: "text-gray-900 text-sm" do %>
<span class="font-medium"><%= t(".upgrades.latest_release.title") %></span>
<br>
<span class="text-gray-500">
<%= t(".upgrades.latest_release.description") %>
</span>
<% end %>
</div>
<div class="flex items-center gap-4">
<%= form.radio_button :upgrades_mode, "commit", checked: Setting.upgrades_mode == "auto" && Setting.upgrades_target == "commit", data: { "auto-submit-form-target" => "auto", "autosubmit-trigger-event": "input" } %>
<%= form.label :upgrades_mode_commit, t(".upgrades.latest_commit.title"), class: "text-gray-900 text-sm" do %>
<span class="font-medium"><%= t(".upgrades.latest_commit.title") %></span>
<br>
<span class="text-gray-500">
<%= t(".upgrades.latest_commit.description") %>
</span>
<% end %>
</div>
</div>
</div>
<div>
<h2 class="font-medium mb-1"><%= t(".provider_settings.title") %></h2>
<p class="text-gray-500 text-sm mb-4"><%= t(".render_deploy_hook_description") %></p>
<%= form.url_field :render_deploy_hook, label: t(".render_deploy_hook_label"), placeholder: t(".render_deploy_hook_placeholder"), value: Setting.render_deploy_hook, data: { "auto-submit-form-target" => "auto" } %>
</div>

**Estimated cost:** $5-15 per month

_Docker is not yet supported, but will be soon._
The steps of deploying a commercial VPS instance provisioned with docker are
outlined in the [docker guide](self-hosting/docker.md).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we consolidate all of this into the self-hosting/docker.md file?

This file (docs/self-hosting.md) could be as short as:

## Docker 

**Estimated cost:** $5-15 per month

Please see the [Docker self-hosting guide](self-hosting/docker.md) for detailed setup instructions.

And then self-hosting/docker.md would provide these sections:

# Self Hosting Maybe with Docker

## Quick Start

[Concise list of commands to run]

## Prerequisites and Setup

[Required deps for fresh VPS]

## Running the app

[Instructions for getting the app started with compose]

## Updating the App

[Explain the flow of pulling updates by tag / commit and updating instance]

## Where should I host?

### Commercial VPS
### One-Click VPS
### Standalone Image

## Troubleshooting 

[We can leave mostly blank for now, but this will be a catch-all]

tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

- name: Prune all untagged docker images
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be wrong, but I don't think Github charges anything for storage on GHCR for public repos?

Either way, I think we can remove this final step here for simplicity.

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
endpoint: builders
Copy link
Collaborator

@zachgoll zachgoll Apr 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove these two steps? It looks like the ubuntu-latest runner already has Docker-Buildx 0.14.0 installed:

https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md#tools

Copy link
Collaborator

@zachgoll zachgoll left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Radu-C-Martin had some time this afternoon to thoroughly test this. I was able to get it all running on a Digital Ocean droplet, so I think the sample compose file looks good.

Just left some comments around config and documentation that I think we should address before merging this in.

type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}.{{minor}}.{{patch}}
type=ref,event=branch
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking the only two tags that we'll need to support for now are the "latest" and "release" tags. By using default configs for metadata-action, I believe we can simplify to this:

with:
  images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
  tags: |
    # set latest tag for main branch
    type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}

    # set semver tag for versioned tags
    type=semver,pattern={{version}}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my experience, the latest tag always points to the latest release. Do you plan for the main branch to always be production-ready?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In theory (since we're using trunk-based development), yes, main should always be production ready. If it's not, the checks should fail and the Docker image should not be published.

I would expect latest to represent the most recently published image, which in our case, would always be the latest commit.

But there probably is some value in providing an alias for self-hosters who want to use the "latest release". What do you think of using the following tag scheme?

  • latest - the latest published image (i.e. main)
  • stable - the latest published release
  • [release_semver_tag] - the semver tag of the release
  • [commit_sha] - the commit sha of the docker image so self-hoster can lock into a certain version

So something like:

tags:
  # Explicit commit sha and version tags
  type=sha
  type=semver,pattern={{version}}

  # Aliases
  type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
  type=raw,value=stable,enable=${{ startsWith(github.ref, 'refs/tags/v') }}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as the tags are semvar compatible, the existing tag scheme is very nice to be able to select an upgrade schedule using auto image pulls. One can have maybe:latest which always auto upgrades (and should not be done), while one can do maybe:2 or maybe:2.3 and get the patch upgrades.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's how I've seen it done in other projects. It's intuitive, and also works well with renovate/dependabot. But I guess that would impose stricter adherence to semver versioning to be totally useful, so it's more for the core maintainers to decide. It can always be added later :D

- .env
environment:
DB_HOST: "postgres"
HOSTING_PLATFORM: "localhost"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to this, we will need to hide both "auto updates" and "provider settings" in the self hosted settings since these do not apply to an app running via docker compose:

<%= settings_section title: t(".general_settings_title") do %>
<%= form_with model: Setting.new, url: settings_hosting_path, method: :patch, local: true, html: { class: "space-y-6", data: { controller: "auto-submit-form", "auto-submit-form-trigger-event-value" => "blur" } } do |form| %>
<div>
<h2 class="font-medium mb-1"><%= t(".upgrades.title") %></h2>
<p class="text-gray-500 text-sm mb-4"><%= t(".upgrades.description") %></p>
<div class="space-y-4">
<div class="flex items-center gap-4">
<%= form.radio_button :upgrades_mode, "manual", checked: Setting.upgrades_mode == "manual", data: { "auto-submit-form-target" => "auto", "autosubmit-trigger-event": "input" } %>
<%= form.label :upgrades_mode_manual, t(".upgrades.manual.title"), class: "text-gray-900 text-sm" do %>
<span class="font-medium"><%= t(".upgrades.manual.title") %></span>
<br>
<span class="text-gray-500">
<%= t(".upgrades.manual.description") %>
</span>
<% end %>
</div>
<div class="flex items-center gap-4">
<%= form.radio_button :upgrades_mode, "release", checked: Setting.upgrades_mode == "auto" && Setting.upgrades_target == "release", data: { "auto-submit-form-target" => "auto", "autosubmit-trigger-event": "input" } %>
<%= form.label :upgrades_mode_release, t(".upgrades.latest_release.title"), class: "text-gray-900 text-sm" do %>
<span class="font-medium"><%= t(".upgrades.latest_release.title") %></span>
<br>
<span class="text-gray-500">
<%= t(".upgrades.latest_release.description") %>
</span>
<% end %>
</div>
<div class="flex items-center gap-4">
<%= form.radio_button :upgrades_mode, "commit", checked: Setting.upgrades_mode == "auto" && Setting.upgrades_target == "commit", data: { "auto-submit-form-target" => "auto", "autosubmit-trigger-event": "input" } %>
<%= form.label :upgrades_mode_commit, t(".upgrades.latest_commit.title"), class: "text-gray-900 text-sm" do %>
<span class="font-medium"><%= t(".upgrades.latest_commit.title") %></span>
<br>
<span class="text-gray-500">
<%= t(".upgrades.latest_commit.description") %>
</span>
<% end %>
</div>
</div>
</div>
<div>
<h2 class="font-medium mb-1"><%= t(".provider_settings.title") %></h2>
<p class="text-gray-500 text-sm mb-4"><%= t(".render_deploy_hook_description") %></p>
<%= form.url_field :render_deploy_hook, label: t(".render_deploy_hook_label"), placeholder: t(".render_deploy_hook_placeholder"), value: Setting.render_deploy_hook, data: { "auto-submit-form-target" => "auto" } %>
</div>

app:
image: ghcr.io/maybe-finance/maybe:latest
ports:
- 3000:3000

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this should be:

Suggested change
- 3000:3000
- "127.0.0.1:3000:3000"

so the Rails backend is not exposed to the internet without a reverse proxy in front of it

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that's needed, and even then if you want to stop it being exposed to the host (not internet, since that would require port-forwarding) just don't bind the ports and use a reverse proxy container.

Copy link

@Quintasan Quintasan May 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

being exposed to the host (not internet, since that would require port-forwarding)

@gantoine I think your vision of how this works is incorrect, especially if you're talking about servers that are exposed to the internet directly.

3000:3000 is equivalent to 0.0.0.0:3000:3000 which means listen on port 3000 on ALL interfaces so if your server has a network interface that's exposed to the internet (literally any VPS) then it will accept incoming connections on port 3000 from virtually anyone. That's not a safe/sane default.

just don't bind the ports and use a reverse proxy container

That's another assumption that's going to make it harder for anyone interested in self-hosting, since your reverse proxy container will listen on ports 80 and 443, and you will have to put any/all subsequent containers in the same Docker network and configure the reverse proxy serving your Maybe instance to serve your other containers or use some convoluted setup involving Traefik and labeling stuff.

I, for example, have a Caddy on the host doing all the reverse proxying to all containers I have on my server like Immich, Maybe, Nextcloud and a ton of other stuff.

Copy link
Collaborator

@zachgoll zachgoll May 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to confirm—you're optimizing for a self-hosting scenario here where you're running this app on your local machine and never want it exposed to the internet?

In your reverse-proxy setup are you assuming you'd run a container (e.g. Traefik), or would you be setting up something like Nginx on the host machine instead?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zachgoll Me? No, I'm optimizing for any sane self-hosting solution - there's a reverse proxy in front of the Rails application. If we put a reverse proxy in the Docker Compose configuration and the self-hosting user has some other services on the server, then they have two options.

  1. Modify the provided docker-compose.yml so the reverse proxy doesn't listen on ports 80 and 443 and point their preferred reverse proxy to... reverse proxy to our reverse proxy
  2. Dump all their other containerized services into the same Docker network and reconfigure our reverse proxy to work with their services.

Maybe there's some convoluted setup leveraging Treafik, but that's not we are aiming for

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m just a self-hosting bystander here, but I would tend to agree with @gantoine. I’ve never seen Docker compose set ups specifically bind the network interface. And, even on a VPS set up, you would need to specifically allow that port in the firewall; at least that’s how it has been for any fresh box I have installed.

I also think if most people are using a Docker set up, there’s no real need to “dumb it down”. They should already understand the risks with simple networking concepts like ports and how to securely access your apps over the internet. I think the Render deploy option serves that purpose well.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A very, very simplified example:

# docker-compose.yml
# Ours will be slightly more verbose
services:
  backend:
    image: traefik/whoami
    command:
      - --port=6666
    ports:
      - "127.0.0.1:6666:6666"
# docker-compose.override.yml
services:
  caddy:
    image: caddy:alpine
    command: caddy reverse-proxy --from <domain to serve on> --to backend:6666
    ports:
      - 80:80
      - 443:443

If you docker-compose up -d with those two files, then you end up with a backend container and a reverse proxy for it with automatic HTTPS. We can supply a custom Caddyfile OR use Traefik as the reverse proxy if we need a more complicated setup

Copy link

@Quintasan Quintasan May 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve never seen Docker compose set ups specifically bind the network interface. And, even on a VPS set up, you would need to specifically allow that port in the firewall; at least that’s how it has been for any fresh box I have installed.

@csmith1210 All OVH and Hetzner boxes I have had so far accept all connections on all ports, unless you explicitly configure them not to do so. Assuming your boxes don't (which is a good thing) accept all connections, then binding to 127.0.0.1:<port> changes exactly nothing in your case, while being a sane default for all boxes which don't have a firewall. I remain unconvinced that binding to 0.0.0.0 and exposing the backend container directly to the internet is a good idea.

I also think if most people are using a Docker set up, there’s no real need to “dumb it down”.

I don't think we're dumbing anything down here. We're trying to provide sane and secure defaults while giving people option to just run it without any tinkering. If someone wants to tinker with this setup, then they have all the escape hatches (docker-compose.override.yml and the ability to bring their own reverse proxy)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

binding to 127.0.0.1: changes exactly nothing in your case, while being a sane default for all boxes which don't have a firewall.

This is a fair point, and as it personally wouldn't affect me (as I know how to edit a compose file) I think it's fine to set it that way in the default. 👍🏼

Copy link
Collaborator

@zachgoll zachgoll May 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @Quintasan's proposal here makes sense overall:

  1. Give a sane default that "just works"
  2. Provide escape hatches for those with the knowledge and/or motivation to customize

Still not 100% certain on the port binding (mostly because I've never seen or done this), but I'd rather be conservative to start and then make modifications (or allow the user to make those mods) if needed.

# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
config.force_ssl = true
config.force_ssl = ENV["DISABLE_SSL"].blank?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
config.force_ssl = ENV["DISABLE_SSL"].blank?
config.force_ssl = ENV.include?("FORCE_SSL")

I think the default assumption is that you're using a reverse proxy to terminate incoming HTTPS traffic.

Copy link
Collaborator

@zachgoll zachgoll May 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've been back and forth on this one quite a bit, but as I was going back through prior discussions, found this comment, which IMO is the clearest solution offered to this:

#308 (comment)

config.force_ssl = ActiveModel::Type::Boolean.new.cast(ENV.fetch('RAILS_FORCE_SSL', true))
config.assume_ssl = ActiveModel::Type::Boolean.new.cast(ENV.fetch('RAILS_ASSUME_SSL', true))

@Radu-C-Martin Radu-C-Martin requested a review from zachgoll May 6, 2024 06:49
@zachgoll
Copy link
Collaborator

zachgoll commented May 6, 2024

@Radu-C-Martin this looks good, thanks for the changes! The documentation and overall structure is good with me.

If I'm not mistaken, the only pending item we have left is the reverse proxy config and the "override" file mentioned by @Quintasan. I think this may be a good follow-up PR.

Unless there are objections, I think we can merge this as-is so we can start getting some images published to GHCR for self-hosters to start playing around with.

@zachgoll zachgoll merged commit 930dc26 into maybe-finance:main May 6, 2024
4 checks passed
@Radu-C-Martin
Copy link
Contributor Author

Hey, Zach! Thanks for merging my PR 🙂 I tried accessing the image and apparently I'm not authorized. I think there could be some settings missing in the organization settings? (this is what I found, but could be out of date: https://github.com/orgs/community/discussions/26014, I've never had to deal this yet). I see that the CI passes correctly, so I assume for you the image works at least?

@zachgoll
Copy link
Collaborator

zachgoll commented May 7, 2024

@Radu-C-Martin thanks for the heads up, we'll get the org setting updated shortly.

@Radu-C-Martin
Copy link
Contributor Author

All good now!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
💻 Self Hosted only Issues pertaining to self-hosted versions of Maybe
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants