Elixir 1.19.5 with Erlang/OTP 28 (CentOS Stream 9) AMI Administrator Guide
1. Quick Start Information
Connection Methods:
- Access the instance via SSH using the
ec2-useruser. Usesudoto run commands requiring root privileges. To switch to the root user, usesudo su - root.
Install Information:
- OS: CentOS Stream 9
- Erlang/OTP version: 28.3.1
- Elixir version: 1.19.5
- Phoenix Framework: v1.8.3
- Erlang Install Directory:
/usr/local/erlang - Elixir Install Directory:
/usr/local/elixir
Quick Verification Commands:
- Check Erlang version:
erl -version - Check Elixir version:
elixir --version - Launch interactive shell:
iex - Check Phoenix installer:
mix phx.new --version
Development Tools:
- Erlang Compiler:
erlc - Elixir Compiler:
elixirc - Mix Build Tool:
mix - Interactive Shell:
iex - Script Runner:
elixirandescript
Firewall Configuration:
- Please allow SSH port 22.
- For security, it is recommended to limit SSH access to trusted IPs only.
2. Overview
Welcome to this Elixir 1.19.5 with Erlang/OTP 28 AMI. This image is based on CentOS Stream 9 and provides a complete, production-ready development environment for building high-performance, fault-tolerant applications.
This guide explains how to use this AMI and details its internal configuration.
What is Elixir?
Elixir is a dynamic, functional programming language designed for building scalable and maintainable applications. It runs on the Erlang Virtual Machine (BEAM), known for running low-latency, distributed, and fault-tolerant systems.
What is Erlang/OTP?
Erlang/OTP is a programming platform for building massively scalable soft real-time systems with requirements on high availability. OTP (Open Telecom Platform) is a collection of libraries, tools, and design principles for building robust applications.
Core Features of This AMI:
- Erlang OTP 28.3.1: Compiled from source with JIT (Just-In-Time) compilation for superior performance
- Elixir 1.19.5: Official precompiled release with full OTP 28 compatibility
- Phoenix Framework: v1.8.3 installer included for rapid web development
- OpenSSL 3.5.5: Modern cryptographic support for HTTPS and secure communications
- Development Tools: Complete toolchain including Mix, Hex, Rebar3
- Optimized Configuration: Production-ready settings without unnecessary GUI dependencies
Target Use Cases:
- Building real-time web applications with Phoenix Framework
- Developing distributed systems and microservices
- Creating fault-tolerant backend services
- Prototyping concurrent and parallel applications
- Learning functional programming patterns
3. First Launch & Verification
Step 1: Connect to Your Instance
- Launch your instance in your cloud provider's console (e.g., AWS EC2)
- Ensure SSH port 22 is allowed in your security group
- Connect via SSH:
ssh -i your-key.pem ec2-user@YOUR_PUBLIC_IP
Step 2: Verify Erlang Installation
Check the Erlang version:
erl -version
Expected Output:
Erlang (SMP,ASYNC_THREADS) (BEAM) emulator version 16.2
The output confirms:
- SMP: Symmetric Multi-Processing enabled (uses all CPU cores)
- ASYNC_THREADS: Asynchronous I/O thread pool enabled
- BEAM: Erlang Virtual Machine
- 16.2: ERTS (Erlang Runtime System) version
Step 3: Verify Elixir Installation
Check the Elixir version:
elixir --version
Expected Output:
Erlang/OTP 28 [erts-16.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit:ns]
Elixir 1.19.5 (compiled with Erlang/OTP 28)
Key information:
- [source]: Compiled from source code (not package manager)
- [64-bit]: 64-bit architecture
- [smp:4:4]: 4 schedulers on 4 cores
- [jit:ns]: JIT compiler enabled (native stack mode)
- compiled with Erlang/OTP 28: Version compatibility verified
Step 4: Test Interactive Shell
Launch the Elixir interactive shell:
iex
Try some commands:
iex(1)> 100 + 200
300
iex(2)> String.upcase("hello elixir")
"HELLO ELIXIR"
iex(3)> Enum.map([1, 2, 3], fn x -> x * 2 end)
[2, 4, 6]
Exit with Ctrl+C twice.
Step 5: Verify Cryptographic Support
Test OpenSSL integration:
elixir -e "IO.inspect :crypto.info()"
Expected Output:
%{
otp_crypto_version: ~c"5.8",
compile_type: :normal,
link_type: :dynamic,
cryptolib_version_compiled: ~c"OpenSSL 3.5.5 27 Jan 2026",
cryptolib_version_linked: ~c"OpenSSL 3.5.5 27 Jan 2026",
fips_provider_available: false
}
This confirms HTTPS and SSL/TLS functionality is fully operational.
Step 6: Verify Phoenix Framework
Check the Phoenix installer:
mix phx.new --version
Expected Output:
Phoenix installer v1.8.3
Step 7: Create and Run a Test Script
Create a simple Elixir script:
echo 'IO.puts "Success! Elixir 1.19.5 is running on OTP 28."' > test.exs
Run it:
elixir test.exs
Expected Output:
Success! Elixir 1.19.5 is running on OTP 28.
Clean up:
rm test.exs
4. Architecture & Detailed Configuration
4.1. Component Locations
| Component | Installation Path | Purpose |
|---|---|---|
| Erlang Home | /usr/local/erlang/ | Erlang runtime and libraries |
| Erlang Binaries | /usr/local/erlang/bin/ | erl, erlc, escript, ct_run |
| Elixir Home | /usr/local/elixir/ | Elixir language and standard library |
| Elixir Binaries | /usr/local/elixir/bin/ | elixir, elixirc, iex, mix |
| Mix Archives | ~/.mix/ | Hex package manager and Phoenix generator |
| System Symlinks | /usr/bin/ | Global command aliases |
4.2. Symbolic Links Configuration
All executables are linked to /usr/bin/ for global access:
Erlang Symlinks:
/usr/bin/erl -> /usr/local/erlang/bin/erl
/usr/bin/erlc -> /usr/local/erlang/bin/erlc
/usr/bin/escript -> /usr/local/erlang/bin/escript
/usr/bin/ct_run -> /usr/local/erlang/bin/ct_run
Elixir Symlinks:
/usr/bin/elixir -> /usr/local/elixir/bin/elixir
/usr/bin/elixirc -> /usr/local/elixir/bin/elixirc
/usr/bin/iex -> /usr/local/elixir/bin/iex
/usr/bin/mix -> /usr/local/elixir/bin/mix
This allows you to run commands from any directory without setting PATH.
4.3. Erlang OTP Compilation Configuration
Erlang was compiled from source with the following options:
./configure --prefix=/usr/local/erlang --without-javac --without-wx
Configuration Flags Explained:
--prefix=/usr/local/erlang: Install location--without-javac: Exclude Java support (not needed for server applications)--without-wx: Exclude wxWidgets GUI (reduces dependencies on headless servers)
Why Compile from Source?
- JIT Optimization: Enables Just-In-Time compilation for 20-50% performance improvement
- Custom Configuration: Fine-tuned for server environments
- Latest Version: Get OTP 28 features immediately (not available in CentOS repos)
- Production Quality: Same build process used by Erlang Solutions and WhatsApp
4.4. Elixir Installation Method
Elixir uses the official precompiled package specifically built for OTP 28:
- Source:
https://github.com/elixir-lang/elixir/releases/download/v1.19.5/elixir-otp-28.zip - Why Precompiled: Guarantees binary compatibility with Erlang OTP 28
- No Compilation: Instant installation, no build tools required
4.5. System Dependencies
The following packages were installed to support compilation and runtime:
# Development Tools
Development Tools group (gcc, make, autoconf, etc.)
# Required Libraries
openssl-devel # HTTPS and cryptographic functions
ncurses-devel # Terminal UI support for Erlang shell
wget # Downloading source files
tar # Archive extraction
git # Version control (optional but recommended)
unzip # Elixir package extraction
5. How-To-Create: Building This AMI from Scratch
This section explains exactly how this AMI was created, allowing you to:
- Understand the configuration
- Reproduce the setup on other systems
- Customize the installation
Step 1: System Preparation
Update the system and install development tools:
# Update all packages
sudo dnf update -y
# Install development tools (gcc, make, etc.)
sudo dnf groupinstall "Development Tools" -y
# Install core dependencies
sudo dnf install -y openssl-devel ncurses-devel wget tar git unzip
Why These Packages:
openssl-devel: Required for Erlang's:cryptomodule (HTTPS support)ncurses-devel: Required for Erlang shell features (color output, line editing)unzip: Required to extract the Elixir precompiled packageDevelopment Tools: Provides gcc, make, autoconf needed to compile Erlang
Step 2: Compile Erlang OTP 28.3.1
Download the official source code:
wget https://github.com/erlang/otp/releases/download/OTP-28.3.1/otp_src_28.3.1.tar.gz
Extract the archive:
tar -xzf otp_src_28.3.1.tar.gz
cd otp_src_28.3.1
Configure the build:
./configure --prefix=/usr/local/erlang --without-javac --without-wx
Configuration Deep Dive:
--prefix=/usr/local/erlang: Installs all files under/usr/local/erlang/--without-javac: Skips Java interface compilation (saves time, reduces dependencies)--without-wx: Skips wxWidgets GUI toolkit (not needed for server environments)
What Gets Built:
- BEAM virtual machine with JIT compiler
- Standard library (stdlib, kernel, compiler, etc.)
- Crypto module with OpenSSL integration
- Network modules (ssl, inets, ssh)
- Database modules (mnesia, odbc)
Compile using all available CPU cores:
make -j$(nproc)
Compilation Time:
- t3.micro (1 vCPU): ~45 minutes
- t3.small (2 vCPU): ~25 minutes
- t3.medium (4 vCPU): ~15 minutes
Install to /usr/local/erlang/:
sudo make install
Create global symbolic links:
sudo ln -sf /usr/local/erlang/bin/erl /usr/bin/erl
sudo ln -sf /usr/local/erlang/bin/erlc /usr/bin/erlc
sudo ln -sf /usr/local/erlang/bin/escript /usr/bin/escript
sudo ln -sf /usr/local/erlang/bin/ct_run /usr/bin/ct_run
Verify the installation:
erl -version
Expected Output:
Erlang (SMP,ASYNC_THREADS) (BEAM) emulator version 16.2
Step 3: Install Elixir 1.19.5
Return to the home directory:
cd ~
Download the precompiled Elixir package for OTP 28:
wget https://github.com/elixir-lang/elixir/releases/download/v1.19.5/elixir-otp-28.zip
Why This Specific Package:
The Elixir team provides multiple precompiled packages per release:
elixir-otp-27.zip- For Erlang/OTP 27elixir-otp-28.zip- For Erlang/OTP 28 (this one)elixir-otp-29.zip- For future OTP 29
Using the correct package ensures binary compatibility and prevents version mismatch errors.
Create the installation directory:
sudo mkdir -p /usr/local/elixir
Extract the package:
sudo unzip elixir-otp-28.zip -d /usr/local/elixir
Package Contents:
/usr/local/elixir/bin/- Executables (elixir, elixirc, iex, mix)/usr/local/elixir/lib/- Standard library (Kernel, Enum, String, etc.)/usr/local/elixir/man/- Manual pages
Create global symbolic links:
sudo ln -sf /usr/local/elixir/bin/iex /usr/bin/iex
sudo ln -sf /usr/local/elixir/bin/mix /usr/bin/mix
sudo ln -sf /usr/local/elixir/bin/elixir /usr/bin/elixir
sudo ln -sf /usr/local/elixir/bin/elixirc /usr/bin/elixirc
Verify the installation:
elixir --version
Expected Output:
Erlang/OTP 28 [erts-16.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit:ns]
Elixir 1.19.5 (compiled with Erlang/OTP 28)
Critical Verification:
The second line must say compiled with Erlang/OTP 28. If it shows a different version, you downloaded the wrong package.
Step 4: Install Phoenix Framework Tools
Install Hex package manager:
mix local.hex --force
What is Hex:
Hex is Elixir's package manager, similar to npm for Node.js or pip for Python. It manages dependencies defined in mix.exs files.
Install Rebar3 build tool:
mix local.rebar --force
What is Rebar3:
Rebar3 is Erlang's build tool. Some Elixir packages include Erlang code or NIFs (Native Implemented Functions) that require Rebar3 to compile.
Install Phoenix project generator:
mix archive.install hex phx_new --force
What is phx_new:
This Mix archive provides the mix phx.new command for scaffolding new Phoenix web applications.
Verify Phoenix installer:
mix phx.new --version
Expected Output:
Phoenix installer v1.8.3
Step 5: Cleanup (Critical for AMI Size Reduction)
Remove Erlang source code and archive:
cd ~
rm -rf otp_src_28.3.1
rm -f otp_src_28.3.1.tar.gz
Space Saved: ~500 MB
Remove Elixir archive:
rm -f elixir-otp-28.zip
Space Saved: ~15 MB
Clean system package cache:
sudo dnf clean all
Space Saved: ~200 MB
Clear log files:
sudo truncate -s 0 /var/log/messages
sudo truncate -s 0 /var/log/secure
sudo truncate -s 0 /var/log/dnf.log
Clear shell history:
history -c
rm -f ~/.bash_history
Total Space Saved: ~700+ MB
This cleanup is essential before creating an AMI snapshot. Without it, your AMI will contain temporary build artifacts that buyers will never use.
6. Using the Development Environment
6.1. Creating a New Phoenix Web Application
Generate a new Phoenix project:
mix phx.new my_app
You'll be prompted:
Fetch and install dependencies? [Yn]
Type Y and press Enter.
Navigate to the project:
cd my_app
Start the development server:
mix phx.server
Expected Output:
[info] Running MyAppWeb.Endpoint with cowboy 2.12.0 at http://localhost:4000
Access the Application:
If running locally: http://localhost:4000
If on a remote server:
- Open port 4000 in your security group
- Visit
http://YOUR_PUBLIC_IP:4000
6.2. Interactive Development with IEx
Start a Phoenix server with interactive shell:
iex -S mix phx.server
Now you can interact with your application while it's running:
iex(1)> MyApp.Repo.all(MyApp.User) # Query database
iex(2)> MyAppWeb.Endpoint.config(:url) # Check config
iex(3)> recompile() # Recompile code changes
6.3. Running Tests
Phoenix projects include ExUnit test framework:
mix test
Run with coverage:
mix test --cover
Run specific test file:
mix test test/my_app_web/controllers/page_controller_test.exs
6.4. Database Management
Create database:
mix ecto.create
Run migrations:
mix ecto.migrate
Rollback migration:
mix ecto.rollback
Reset database (drop, create, migrate):
mix ecto.reset
6.5. Dependency Management
Add a dependency to mix.exs:
defp deps do
[
{:jason, "~> 1.4"} # Add this line
]
end
Install new dependencies:
mix deps.get
Update all dependencies:
mix deps.update --all
List dependencies:
mix deps
6.6. Building for Production
Compile for production:
MIX_ENV=prod mix compile
Build a release:
MIX_ENV=prod mix release
The release will be in _build/prod/rel/my_app/.
Start the release:
_build/prod/rel/my_app/bin/my_app start
7. Important File Locations
| Path | Purpose | Notes |
|---|---|---|
/usr/local/erlang/ | Erlang installation | Contains bin/, lib/, erts-16.2/ |
/usr/local/elixir/ | Elixir installation | Contains bin/, lib/, man/ |
/usr/bin/erl | Erlang shell symlink | Global access |
/usr/bin/elixir | Elixir runtime symlink | Global access |
/usr/bin/iex | Interactive shell symlink | Global access |
/usr/bin/mix | Build tool symlink | Global access |
~/.mix/ | Mix configuration and archives | Per-user, auto-created |
~/.hex/ | Hex package cache | Per-user, auto-created |
8. Troubleshooting
Problem: elixir: command not found
Cause: Symbolic links not created or PATH not updated.
Solution:
# Verify symlinks exist
ls -la /usr/bin/elixir
ls -la /usr/bin/iex
ls -la /usr/bin/mix
# If missing, recreate them
sudo ln -sf /usr/local/elixir/bin/elixir /usr/bin/elixir
sudo ln -sf /usr/local/elixir/bin/iex /usr/bin/iex
sudo ln -sf /usr/local/elixir/bin/mix /usr/bin/mix
Problem: Incompatible Erlang/OTP version error
Cause: Elixir package doesn't match Erlang version.
Solution:
Check versions:
erl -version
elixir --version
If Erlang is OTP 28, you must use elixir-otp-28.zip. Re-download the correct package.
Problem: mix commands fail with SSL errors
Cause: Hex package manager not installed or SSL certificates missing.
Solution:
# Reinstall Hex
mix local.hex --force
# Update CA certificates
sudo dnf update -y ca-certificates
Problem: Phoenix server fails to start on port 4000
Cause: Port already in use or firewall blocking.
Check if port is in use:
sudo netstat -tulpn | grep 4000
Kill existing process:
sudo kill -9 <PID>
Or change port in config/dev.exs:
config :my_app, MyAppWeb.Endpoint,
http: [ip: {0, 0, 0, 0}, port: 5000] # Change to 5000
Problem: mix deps.get fails with network errors
Cause: Hex package server unreachable or proxy issues.
Solution:
# Use CDN mirror
export HEX_MIRROR=https://hexpm.global.ssl.fastly.net
# Retry
mix deps.get
Problem: Application crashes with eaddrinuse error
Cause: Another process is using the same port.
Solution:
Find and stop the conflicting process:
sudo lsof -i :4000
sudo kill <PID>
Or restart the server with a different port:
PORT=5000 mix phx.server
9. Advanced Topics
9.1. Concurrent Programming with Tasks
Elixir makes concurrency simple with the Task module:
# Spawn 1000 concurrent tasks
tasks = for i <- 1..1000 do
Task.async(fn ->
:timer.sleep(1000)
i * 2
end)
end
# Wait for all results
results = Task.await_many(tasks)
This code spawns 1000 lightweight processes (not OS threads), each sleeping 1 second, but completes in ~1 second total.
9.2. Building Distributed Systems
Start multiple Erlang nodes:
Node 1:
iex --sname node1 --cookie secret
Node 2 (in another terminal):
iex --sname node2 --cookie secret
Connect nodes:
Node.connect(:node1@hostname)
Node.list() # See connected nodes
Call functions remotely:
:rpc.call(:node1@hostname, IO, :puts, ["Hello from node2"])
9.3. Performance Monitoring
Observer is a GUI tool for monitoring BEAM:
# Start from iex
:observer.start()
Note: This requires X11 forwarding for remote servers:
ssh -X ec2-user@YOUR_IP
For production servers, use alternative tools:
:etop.start()- Terminal-based process monitor- Erlang's
:reconlibrary
9.4. Memory and CPU Profiling
Check memory usage:
:erlang.memory()
Profile CPU usage:
# Start profiler
:fprof.start()
# Profile a function
:fprof.apply(MyModule, :my_function, [arg1, arg2])
# Analyze results
:fprof.profile()
:fprof.analyse()
9.5. Hot Code Reloading
One of Erlang/OTP's killer features is updating code without stopping the system:
# In production server
iex --sname prod@localhost --cookie prod_secret
# Compile new module version
c("lib/my_app/my_module.ex")
# The new code is now live, old processes still use old version
# New processes use new version
9.6. Optimizing Compilation
For faster development, use parallel compilation in mix.exs:
def project do
[
# ... other settings
compilers: Mix.compilers(),
consolidate_protocols: Mix.env() != :dev # Faster dev compilation
]
end
9.7. Understanding the BEAM VM
Check scheduler information:
:erlang.system_info(:schedulers)
:erlang.system_info(:schedulers_online)
Check process count:
:erlang.system_info(:process_count)
:erlang.system_info(:process_limit) # Default: 262,144
Check atom count (atoms are never garbage collected):
:erlang.system_info(:atom_count)
:erlang.system_info(:atom_limit) # Default: 1,048,576
10. Security Considerations
10.1. Production Security Checklist
When deploying Elixir/Phoenix applications:
- Set SECRET_KEY_BASE: Use
mix phx.gen.secretto generate a secure key - Enable HTTPS: Never run production apps over HTTP
- Disable Debug: Set
debug_errors: falseinprod.exs - Enable CSRF Protection: Phoenix enables this by default
- Use Environment Variables: Never hardcode secrets
- Limit SSH Access: Use key-based authentication only
- Update Dependencies: Run
mix hex.auditto check for vulnerabilities
10.2. Firewall Best Practices
Only expose necessary ports:
# SSH (restrict to your IP)
Source: YOUR_IP/32, Port: 22
# Web application (if public)
Source: 0.0.0.0/0, Port: 443 (HTTPS)
# Do NOT expose these ports:
# - 4369 (EPMD - Erlang Port Mapper Daemon)
# - 9000-9100 (Distributed Erlang)
# - 4000 (Development server)
10.3. Running as Non-Root User
Always run production applications as a non-root user:
# Create application user
sudo useradd -m -s /bin/bash myapp
# Deploy your release to /home/myapp/
sudo cp -r _build/prod/rel/my_app /home/myapp/
sudo chown -R myapp:myapp /home/myapp/
# Run as that user
sudo -u myapp /home/myapp/my_app/bin/my_app start
11. Final Notes
What You've Got
This AMI provides a complete, production-ready Elixir development environment:
- ✅ Erlang OTP 28.3.1 with JIT compilation (20-50% performance boost)
- ✅ Elixir 1.19.5 with full OTP 28 compatibility
- ✅ Phoenix Framework v1.8.3 for rapid web development
- ✅ OpenSSL 3.5.5 for modern cryptography
- ✅ All development tools (Mix, Hex, Rebar3, IEx)
- ✅ Optimized for server workloads (no GUI dependencies)
Recommended Next Steps
-
Learn Elixir Basics:
- Official Guide: https://elixir-lang.org/getting-started/introduction.html
- Elixir School: https://elixirschool.com/
-
Build a Phoenix App:
- Phoenix Guides: https://hexdocs.pm/phoenix/overview.html
- Create your first app:
mix phx.new my_app
-
Explore OTP:
- GenServer for stateful processes
- Supervisors for fault tolerance
- Applications for packaging code
-
Join the Community:
- Elixir Forum: https://elixirforum.com/
- Elixir Slack: https://elixir-slackin.herokuapp.com/
Performance Expectations
Elixir/Erlang can handle:
- Millions of concurrent connections (WhatsApp handled 2M+ per server)
- Sub-millisecond latency for most operations
- Soft real-time performance for chat, gaming, IoT
- High availability with 99.9999999% uptime (nine nines)
Cost Optimization
For development:
- t3.micro (1 vCPU, 1GB RAM): Sufficient for learning and small projects
- t3.small (2 vCPU, 2GB RAM): Better for Phoenix apps with databases
For production:
- t3.medium+ (2+ vCPU, 4GB+ RAM): Recommended starting point
- c5.large+: For CPU-intensive workloads
- r5.large+: For memory-intensive workloads
The BEAM VM scales efficiently with CPU cores, so vertical scaling (bigger instances) is often more cost-effective than horizontal scaling.
Enjoy building with Elixir! 🚀