Deploying a Phoenix App with Elixir on Deployxa
Phoenix is the leading Elixir web framework, known for its real-time features (via LiveView and Channels) and excellent performance (via the Erlang VM). It is a favorite of teams building realtime apps (chat, collaboration, dashboards) and high-traffic APIs. But deploying Phoenix has traditionally required writing a Dockerfile (to compile the Elixir code and run it with the Erlang VM), configuring the release, and handling the database connection. Deployxa's zero-config engine eliminates all of that, detecting Phoenix from your mix.exs and configuring the deployment. Here is how to deploy a Phoenix app on Deployxa.
The direct answer is that Deployxa auto-detects Phoenix from your mix.exs (which includes phoenix). It configures the build and start commands: the build command is mix deps.get && mix compile && mix release, the start command is _build/prod/rel/myapp/bin/myapp start, and the runtime is Elixir 1.16 with Erlang 26. You do not write a Dockerfile, you do not configure the release manually, and you do not manage the Erlang VM. The platform handles all of it, just as it does for Spring Boot and Flask apps.
Why Phoenix Is a Great Choice for Realtime Apps
Three reasons explain why Phoenix is a great choice for realtime apps. First, its LiveView feature lets you build realtime, interactive UIs without writing JavaScript, which is a significant productivity boost. For apps that need realtime features (chat, collaboration, dashboards), LiveView is hard to beat. Second, its Channels feature (built on the Erlang VM's process model) handles millions of concurrent WebSocket connections, which makes Phoenix ideal for high-concurrency realtime apps. Third, its performance is excellent. The Erlang VM is designed for high-concurrency, fault-tolerant systems, which means Phoenix apps are fast and reliable. For more on realtime features, see our article on running long-lived WebSockets in Node.js and Go.
The Architecture: Phoenix + Erlang VM + Container
Here is how Deployxa deploys a Phoenix app.
The Phoenix container
The ingestion service detects Phoenix from your mix.exs. It configures the build and start commands:
- Build command: mix deps.get && mix compile && mix release
- Start command: _build/prod/rel/myapp/bin/myapp start
- Runtime: Elixir 1.16 with Erlang 26
The release configuration
Phoenix releases are self-contained packages that include the compiled code, the Erlang VM, and all dependencies. The release is configured via config/runtime.exs, which reads environment variables at runtime.
The reverse proxy
Traefik v3 routes traffic from your custom domain to the Phoenix container, with automatic SSL via Let's Encrypt.
Step-by-Step: Deploying a Phoenix App
Here is the exact workflow for a typical Cursor-generated Phoenix app.
Step 1: Create your Phoenix app
mix phx.new my_app
cd my_app
mix deps.getStep 2: Configure runtime.exs
# config/runtime.exs
import Config
if config_env() == :prod do
database_url = System.get_env("DATABASE_URL") ||
raise "DATABASE_URL is not set"
secret_key_base = System.get_env("SECRET_KEY_BASE") ||
raise "SECRET_KEY_BASE is not set"
config :my_app, MyApp.Repo,
url: database_url,
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")
config :my_app, MyAppWeb.Endpoint,
server: true,
url: [host: System.get_env("PHX_HOST") || "localhost", port: 443, scheme: "https"],
http: [ip: {0, 0, 0, 0}, port: String.to_integer(System.get_env("PORT") || "4000")],
secret_key_base: secret_key_base
endStep 3: Create a simple controller
# lib/my_app_web/controllers/page_controller.ex
defmodule MyAppWeb.PageController do
use MyAppWeb, :controller
def index(conn, _params) do
json(conn, %{message: "Hello, World!"})
end
def health(conn, _params) do
json(conn, %{status: "ok"})
end
endStep 4: Add routes
# lib/my_app_web/router.ex
defmodule MyAppWeb.Router do
use MyAppWeb, :router
pipeline :api do
plug :accepts, ["json"]
end
scope "/", MyAppWeb do
pipe_through :api
get "/", PageController, :index
get "/health", PageController, :health
end
endStep 5: Push to GitHub
git init
git add .
git commit -m "phoenix app"
git remote add origin https://github.com/yourname/my_app.git
git push -u origin mainStep 6: Connect to Deployxa
In the Deployxa dashboard, connect your repository. Deployxa auto-detects Phoenix:
[ingest] Detected Elixir project
[ingest] Framework: phoenix
[ingest] Runtime: elixir 1.16, erlang 26
[ingest] Build command: mix deps.get && mix compile && mix release
[ingest] Start command: _build/prod/rel/my_app/bin/my_app start
[ingest] Port: $PORTStep 7: Configure environment variables
In the Deployxa dashboard, add:
- DATABASE_URL: your Postgres connection string (e.g., ecto://user:pass@host:5432/myapp)
- SECRET_KEY_BASE: a strong secret (generate with mix phx.gen.secret)
- PHX_HOST: your app's domain (e.g., myapp.com)
The pre-flight scanner will warn you if any are missing. For more on environment variables, see our article on the vibe coder's guide to environment variables.
Step 8: Deploy
Click Deploy. The build compiles the Elixir code and creates a release, the container starts the release, and your app is live within 60 to 90 seconds.
Step 9: Add a custom domain
Add a custom domain in the Deployxa dashboard. SSL is provisioned automatically.
Step 10: Verify with deployxa doctor
Run deployxa doctor to verify health. The 14-point readiness engine checks SSL, DNS, environment variables, health endpoints, and container status.
Common Pitfalls and Troubleshooting
The first pitfall is the PORT environment variable. Phoenix apps typically listen on port 4000 by default, but Deployxa assigns a dynamic port via the PORT environment variable. The fix is to set http: [ip: {0, 0, 0, 0}, port: String.to_integer(System.get_env("PORT") || "4000")] in runtime.exs. The second pitfall is the SECRET_KEY_BASE. Phoenix requires a strong secret key base in production, which AI assistants often leave as a default or weak value. The fix is to generate a strong secret with mix phx.gen.secret and set it as the SECRET_KEY_BASE environment variable. The third pitfall is the database URL format. Phoenix uses Ecto, which uses a different URL format from other frameworks (e.g., ecto:// instead of postgresql://). The fix is to use the Ecto format in your DATABASE_URL environment variable. The fourth pitfall is the release configuration. Phoenix releases need to be configured via config/runtime.exs (not config/prod.exs), because runtime.exs is evaluated at runtime (when environment variables are available). The fix is to put your environment-variable-dependent configuration in runtime.exs. The fifth pitfall is the Erlang cookie. Phoenix releases use an Erlang cookie for clustering, which needs to be set consistently across all instances. The fix is to set the ERL_AFLAGS environment variable with the cookie (e.g., -setcookie my-cookie).
Performance: Phoenix vs Node.js vs Go
Phoenix, Node.js, and Go are three leading choices for realtime apps. Phoenix (via the Erlang VM) handles millions of concurrent WebSocket connections, which makes it ideal for high-concurrency realtime apps. Node.js handles thousands of concurrent connections (via the event loop), which is sufficient for most apps. Go handles tens of thousands of concurrent connections (via goroutines), which is between Phoenix and Node.js. For high-concurrency realtime apps, Phoenix is the best choice. For apps that need the Node.js ecosystem, Node.js is the better choice. For apps that need maximum performance and efficiency, Go is the better choice. Deployxa supports all three equally, with the AutoRepairService and the zero-config engine handling each framework automatically. For more on performance, see our article on SPA vs SSR hardware sizing. For more on realtime features, see our article on running long-lived WebSockets.
Advanced Phoenix Patterns
Beyond the basics, Phoenix apps benefit from several advanced patterns. The first is LiveView. LiveView lets you build realtime, interactive UIs without writing JavaScript, which is a significant productivity boost. The second is Channels. Channels (built on the Erlang VM's process model) handle millions of concurrent WebSocket connections, which makes Phoenix ideal for high-concurrency realtime apps. The third is Ecto. Ecto is Phoenix's database wrapper, which provides a DSL for queries and migrations. The fourth is Phoenix Presence. Presence tracks which users are online, which is useful for chat apps and collaboration tools. The fifth is testing. Phoenix has excellent testing support (via ExUnit), which makes it easy to write unit and integration tests. For more on testing, see our article on the testing void. For more on Phoenix deployment, see our articles on deploying a Ruby on Rails app and deploying a Flask app with Gunicorn.
Conclusion: Phoenix Without the Configuration
Phoenix is the leading Elixir web framework, and deploying it should be as simple as pushing to Git. Deployxa's zero-config engine makes it so: no Dockerfile, no release configuration, no Erlang VM management. Stop configuring releases and start shipping.
Ready to deploy your Phoenix app? Drag your project to Deployxa Drop for an instant live preview, or install the CLI with npm i -g @deployxa/cli and deploy from your terminal. For more on framework deep-dives, see our articles on deploying a Spring Boot app and deploying an Express app with PM2. Learn about deploying a Ruby on Rails app and deploying a Flask app with Gunicorn in our companion articles. Explore our free developer tools to speed up your workflow.