Elixir Runtime

Host Elixir applications on Witchly.host with Mix. Deploy from Git, run Phoenix apps, GenServers, or classic OTP releases.

Elixir Runtime

Elixir is a functional, concurrent, high-level language running on the BEAM virtual machine (the same VM as Erlang). It shines at highly-concurrent systems — chat servers, real-time dashboards, Phoenix web apps, GenServer-based workers, and OTP releases. Witchly runs the latest Elixir.

When to pick Elixir

  • You’re building a real-time system (Phoenix LiveView, PubSub, websockets).
  • You need fault tolerance — supervision trees keep your app alive through crashes.
  • You want excellent concurrency primitives without fighting threads.

Quick deploy

  1. dash.witchly.hostDeployLanguagesElixir.
  2. On the Startup tab, set Git Repository Address to your repo URL.
  3. Start — first boot runs mix deps.get then mix run --no-halt.

Startup flow

if [[ -d .git ]] && [[ {{AUTO_UPDATE}} == "1" ]]; then git pull; fi
mix deps.get
mix run --no-halt

mix run --no-halt starts your app and keeps the BEAM running. For Phoenix, you’ll want to customize this (see below).

Variables reference

VariablePurpose
GIT_ADDRESSGit repo URL
GIT_BRANCHGit branch
USER_UPLOAD1 to skip clone
AUTO_UPDATE1 to git pull on each start
GIT_USERNAME, GIT_ACCESS_TOKENPrivate repo credentials

Phoenix app

For a Phoenix project, override the startup command:

if [[ -d .git ]] && [[ {{AUTO_UPDATE}} == "1" ]]; then git pull; fi
mix deps.get --only prod
MIX_ENV=prod mix compile
MIX_ENV=prod mix assets.deploy
MIX_ENV=prod PORT=${SERVER_PORT} mix phx.server

Make sure your config/runtime.exs reads from env:

config :my_app, MyAppWeb.Endpoint,
  http: [ip: {0, 0, 0, 0}, port: String.to_integer(System.get_env("PORT") || "4000")],
  secret_key_base: System.get_env("SECRET_KEY_BASE")

For small, fast, reproducible deploys, build an OTP release:

# Locally or in CI
MIX_ENV=prod mix release
# Creates _build/prod/rel/my_app/

Tar the release, SFTP it to /home/container/, extract, then set the startup to:

./bin/my_app start

OTP releases don’t need Elixir/Mix installed — they’re self-contained BEAM binaries.

Discord bot with Nostrum

# lib/my_bot/consumer.ex
defmodule MyBot.Consumer do
  use Nostrum.Consumer
  alias Nostrum.Api

  def handle_event({:MESSAGE_CREATE, msg, _ws}) do
    case msg.content do
      "!ping" -> Api.create_message(msg.channel_id, "pong")
      _ -> :ignore
    end
  end
end
# mix.exs
defp deps do
  [{:nostrum, "~> 0.9"}]
end

Managing state

Elixir shines with GenServers, Supervisors, and ETS. If you’re storing app state in memory, make sure it’s under a Supervisor so crashes are handled gracefully.

For persistent state, pair with Postgres + Ecto.

Troubleshooting

  • mix: command not found — confirm the Elixir image is fully built; the first deploy can take a minute to be ready.
  • Port not accessible — bind explicitly to 0.0.0.0 in config, not localhost / 127.0.0.1.
  • Hot code reload not working — reloads in dev only; use a staging server with auto-update for dev-like experience.

Next steps

  • Postgres + Ecto for database-backed apps
  • Redis for cross-node PubSub