Docs / Programming & Development / Phoenix Elixir Application

Phoenix Elixir Application

By Admin · Mar 15, 2026 · Updated Apr 23, 2026 · 181 views · 4 min read

Phoenix is an Elixir web framework built on the battle-tested BEAM virtual machine, offering real-time capabilities through LiveView and unmatched fault tolerance via OTP supervision trees. This guide covers deploying a Phoenix application with releases for production use on a VPS.

Installing Elixir and Phoenix

# Install Erlang and Elixir
sudo apt install erlang elixir

# Or use asdf for version management
asdf plugin add erlang
asdf plugin add elixir
asdf install erlang 27.0
asdf install elixir 1.17.0-otp-27
asdf global erlang 27.0
asdf global elixir 1.17.0-otp-27

# Install Phoenix
mix local.hex --force
mix archive.install hex phx_new --force

Creating a Phoenix Application

# Create new project
mix phx.new myapp --database postgres
cd myapp

# Setup database
mix ecto.create
mix ecto.migrate

# Start development server
mix phx.server

Project Structure

myapp/
├── lib/
│   ├── myapp/           # Business logic (contexts)
│   │   ├── accounts.ex  # Accounts context
│   │   └── accounts/
│   │       └── user.ex  # User schema
│   ├── myapp_web/       # Web layer
│   │   ├── controllers/
│   │   ├── live/        # LiveView modules
│   │   ├── components/
│   │   ├── router.ex
│   │   └── endpoint.ex
│   ├── myapp.ex
│   └── myapp_web.ex
├── config/
│   ├── config.exs
│   ├── dev.exs
│   ├── prod.exs
│   └── runtime.exs     # Runtime configuration
├── priv/
│   ├── repo/migrations/
│   └── static/
└── mix.exs

Building a Release

# Configure runtime.exs for production
# config/runtime.exs
import Config

if config_env() == :prod do
  config :myapp, MyApp.Repo,
    url: System.get_env("DATABASE_URL"),
    pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")

  config :myapp, MyAppWeb.Endpoint,
    url: [host: System.get_env("PHX_HOST"), port: 443, scheme: "https"],
    http: [
      ip: {0, 0, 0, 0},
      port: String.to_integer(System.get_env("PORT") || "4000")
    ],
    secret_key_base: System.get_env("SECRET_KEY_BASE")
end
# Build the release
MIX_ENV=prod mix deps.get --only prod
MIX_ENV=prod mix compile
MIX_ENV=prod mix assets.deploy
MIX_ENV=prod mix release

# The release is at _build/prod/rel/myapp/
# It includes the BEAM VM — no Erlang/Elixir needed on the server

Deploying the Release

# Copy release to VPS
tar czf myapp.tar.gz -C _build/prod/rel/myapp .
scp myapp.tar.gz user@vps:/opt/myapp/

# On VPS
cd /opt/myapp && tar xzf myapp.tar.gz

# Set environment variables
export DATABASE_URL="ecto://user:pass@localhost/myapp_prod"
export SECRET_KEY_BASE=$(mix phx.gen.secret)
export PHX_HOST="example.com"
export PORT=4000

# Run migrations
./bin/myapp eval "MyApp.Release.migrate"

# Start the server
./bin/myapp start

Systemd Service

[Unit]
Description=Phoenix Application
After=network.target postgresql.service

[Service]
Type=exec
User=appuser
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/myapp start
ExecStop=/opt/myapp/bin/myapp stop
EnvironmentFile=/opt/myapp/.env
Restart=on-failure
RestartSec=5
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target

LiveView for Real-Time UIs

# Phoenix LiveView provides real-time UI without JavaScript
# lib/myapp_web/live/dashboard_live.ex
defmodule MyAppWeb.DashboardLive do
  use MyAppWeb, :live_view

  @impl true
  def mount(_params, _session, socket) do
    if connected?(socket), do: :timer.send_interval(5000, self(), :tick)
    {:ok, assign(socket, users_count: Accounts.count_users())}
  end

  @impl true
  def handle_info(:tick, socket) do
    {:noreply, assign(socket, users_count: Accounts.count_users())}
  end

  @impl true
  def render(assigns) do
    ~H"""
    <div class="dashboard">
      <h1>Dashboard</h1>
      <p>Active users: <%= @users_count %></p>
    </div>
    """
  end
end

Docker Deployment

FROM elixir:1.17-alpine AS builder
RUN apk add --no-cache build-base git
WORKDIR /app
ENV MIX_ENV=prod
COPY mix.exs mix.lock ./
RUN mix deps.get --only prod && mix deps.compile
COPY config config
COPY lib lib
COPY priv priv
COPY assets assets
RUN mix assets.deploy && mix release

FROM alpine:3.20
RUN apk add --no-cache libstdc++ openssl ncurses-libs
WORKDIR /app
COPY --from=builder /app/_build/prod/rel/myapp ./
USER nobody
CMD ["bin/myapp", "start"]

Summary

Phoenix on Elixir provides fault-tolerant, real-time web applications backed by the BEAM VM. OTP releases create self-contained deployable packages that include the runtime. LiveView eliminates the need for complex JavaScript frameworks for real-time features. The BEAM VM excels at handling thousands of concurrent connections with minimal memory, making it ideal for real-time applications, chat systems, and IoT platforms on modest VPS hardware.

Was this article helpful?