Skip to content

Instantly share code, notes, and snippets.

@cdock1029
Forked from mcrumm/phx_sqlite_fly_launch.md
Last active March 30, 2023 01:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cdock1029/ab0a0c7addec02c780893fd90986535c to your computer and use it in GitHub Desktop.
Save cdock1029/ab0a0c7addec02c780893fd90986535c to your computer and use it in GitHub Desktop.
Phoenix + SQLite Deployment tips + Tailscale private app
$ fly launch --no-deploy --force-machines

The fly launch command generates a Dockerfile, a fly.toml configuration and some release files into your Phoenix app. It will even set SECRET_KEY_BASE for you.

$ fly volumes create myapp_data --size 1

The following represents the proper changes for DATABASE_PATH:

# fly.toml

+[mounts]
+ source = "myapp_data"
+ destination = "/data"

[env]
+ DATABASE_PATH = "/data/myapp_data/my_app_prod.db"
  PHX_HOST = "spicy-burrito-2702.fly.dev"
  PORT = "8080"

Remove the migrate command:

# fly.toml

-[deploy]
-  release_command = "/app/bin/migrate"

So how to run migrations? I invoke the release migrate function from Application.start/2 on the sage advice of @chrismccord:

# lib/my_app/application.ex

  def start(_type, _args) do
    # Run migrations
    MyApp.Release.migrate()

    children = [
      #children...
    ]

    #Supervisor...
  end

Or

create start.sh

#!/bin/bash

set -e

/app/bin/migrate
/app/bin/server

..and modify Dockerfile

COPY --from=builder --chown=nobody:root /app/_build/${MIX_ENV}/rel/my_app ./
+ COPY --chown=nobody:root --chmod=755 start.sh ./

USER nobody

- CMD ["/app/bin/server"]
+ CMD ["/app/start.sh"]

Tailscale

Tailscale docs for running on Fly.io

Create Tailscale auth key and save it as fly secret

flyctl secrets set TAILSCALE_AUTHKEY="tskey-<key>"

Dockerfile changes

ARG BUILDER_IMAGE="hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}"
ARG RUNNER_IMAGE="debian:${DEBIAN_VERSION}"

+ FROM alpine:latest as tailscale
+ WORKDIR /app
+ ENV TSFILE=tailscale_1.38.2_amd64.tgz
+ RUN wget https://pkgs.tailscale.com/stable/${TSFILE} && tar xzf ${TSFILE} --strip-components=1

FROM ${BUILDER_IMAGE} as builder

...

FROM ${RUNNER_IMAGE}

RUN apt-get update -y && apt-get install -y libstdc++6 openssl libncurses5 locales \
+ ca-certificates iptables sudo \
  && apt-get clean && rm -f /var/lib/apt/lists/*_*

+ RUN update-alternatives --set iptables /usr/sbin/iptables-legacy
+ RUN update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy

# Set the locale
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen

...

# Only copy the final release from the build stage
COPY --from=builder --chown=nobody:root /app/_build/${MIX_ENV}/rel/my_app ./
+ COPY --from=tailscale /app/tailscaled /app/tailscaled
+ COPY --from=tailscale /app/tailscale /app/tailscale
+ 
+ RUN mkdir -p /var/run/tailscale /var/cache/tailscale /var/lib/tailscale
+ 
COPY --chown=nobody:root --chmod=755 start.sh ./

- USER nobody

- CMD ["/app/bin/server"]
+ CMD ["/app/start.sh"]

start.sh

#!/bin/bash

set -e

+ echo "Starting Tailscale"
+ /app/tailscaled --state=/var/lib/tailscale/tailscaled.state --socket=/var/run/tailscale/tailscaled.sock &
+ /app/tailscale up --authkey=${TAILSCALE_AUTHKEY} --hostname=my_app

echo "Starting app in start.sh ..."
- /app/bin/migrate
- /app/bin/server
+ sudo -E -u nobody /app/bin/migrate
+ sudo -E -u nobody /app/bin/server

Only allow access from Tailscale network

config/runtime.exs

host = System.get_env("PHX_HOST") || "example.com"
  port = String.to_integer(System.get_env("PORT") || "4000")

  config :my_app, MyAppWeb.Endpoint,
-   url: [host: host, port: 443, scheme: "https"],
+   url: [host: host, port: port, scheme: "http"],
+   check_origin: [
+     "//#{host}:#{port}",
+     "//my_app:#{port}"
+   ],
    http: [
      # Enable IPv6 and bind on all interfaces.
      # Set it to  {0, 0, 0, 0, 0, 0, 0, 1} for local network only access.
      # See the documentation on https://hexdocs.pm/plug_cowboy/Plug.Cowboy.html
      # for details about using IPv6 vs IPv4 and loopback vs public addresses.
      ip: {0, 0, 0, 0, 0, 0, 0, 0},
      port: port
    ],
    secret_key_base: secret_key_base

fly.toml

[env]
  DATABASE_PATH = "/data/my_app_data/my_app_prod.db"
- PHX_HOST = "arbitrary-fly-url-4555.fly.dev"
+ PHX_HOST = "my-device.my-tailnet-name.ts.net"
  PORT = "8080"

- [[services]]
-   internal_port = 8080
-   processes = ["app"]
-   protocol = "tcp"
-   [services.concurrency]
-     hard_limit = 1000
-     soft_limit = 1000
-     type = "connections"

-   [[services.ports]]
-     force_https = true
-     handlers = ["http"]
-     port = 80

-   [[services.ports]]
-     handlers = ["tls", "http"]
-     port = 443
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment