Deploying an Akkoma App
Introduction
Akkoma is a lightweight, federated social networking server that implements the ActivityPub protocol, allowing users to connect and communicate across different instances in the Fediverse. Built with Elixir and the Phoenix framework, Akkoma is a fork of Pleroma that focuses on simplicity, performance, and user privacy.
Akkoma is renowned for its:
- Federation Support: Full ActivityPub protocol implementation for interoperability with Mastodon, Pleroma, and other Fediverse platforms
- Lightweight: Efficient resource usage compared to other federated social networking servers
- Privacy-Focused: Strong emphasis on user privacy and data protection
- Customizable: Extensive theming and configuration options
- Rich Media Support: Support for images, videos, and other media types
- Real-Time Updates: WebSocket support for real-time notifications and updates
- Moderation Tools: Comprehensive moderation and administration features
- Multi-User Support: Support for multiple users and communities on a single instance
- API Access: RESTful API for third-party applications and bots
- Open Source: Fully open-source with active community development
Common use cases include personal social networking instances, community platforms, alternative social media hosting, privacy-focused social networks, and federated communication hubs.
This comprehensive guide walks you through deploying Akkoma on Klutch.sh using a Dockerfile, including detailed installation steps, PostgreSQL database configuration, persistent storage setup, and production-ready best practices for hosting a federated social networking instance.
Prerequisites
Before you begin, ensure you have the following:
- A Klutch.sh account
- A GitHub account with a repository for your Akkoma project
- A PostgreSQL database (can be deployed separately on Klutch.sh or use an external database)
- Docker installed locally for testing (optional but recommended)
- Basic understanding of Elixir, Phoenix, and federated social networking concepts
- A domain name for your instance (recommended for production and federation)
Installation and Setup
Step 1: Create Your Project Directory
First, create a new directory for your Akkoma deployment project:
mkdir akkoma-klutchcd akkoma-klutchgit initStep 2: Clone or Prepare Akkoma Source
You can either clone the official Akkoma repository or prepare your own Akkoma-based application:
# Option 1: Clone the official Akkoma repositorygit clone https://akkoma.dev/AkkomaGang/akkoma.git -b stablecd akkoma
# Option 2: If you have your own Akkoma fork or custom instance# Copy your Akkoma source code to the project directoryStep 3: Create the Dockerfile
Create a Dockerfile in your project root directory. This will define your Akkoma container configuration:
FROM elixir:1.15-alpine AS builder
# Install build dependenciesRUN apk add --no-cache \ build-base \ git \ postgresql-dev \ imagemagick \ ffmpeg \ exiftool \ file-dev \ ncurses-dev
# Set working directoryWORKDIR /app
# Install hex and rebarRUN mix local.hex --force && \ mix local.rebar --force
# Copy mix filesCOPY mix.exs mix.lock* ./COPY config config
# Install dependenciesRUN mix deps.get --only prod && \ mix deps.compile
# Copy application codeCOPY . .
# Build the applicationRUN mix compile && \ mix assets.deploy && \ mix phx.digest
# Production stageFROM elixir:1.15-alpine
# Install runtime dependenciesRUN apk add --no-cache \ postgresql-client \ imagemagick \ ffmpeg \ exiftool \ file \ ncurses \ bash \ curl
# Create akkoma userRUN addgroup -g 1000 -S akkoma && \ adduser -u 1000 -S akkoma -G akkoma
# Set working directoryWORKDIR /app
# Copy built application from builderCOPY --from=builder --chown=akkoma:akkoma /app/_build/prod/rel/akkoma ./relCOPY --from=builder --chown=akkoma:akkoma /app/config ./configCOPY --from=builder --chown=akkoma:akkoma /app/lib ./lib
# Create directories for persistent dataRUN mkdir -p /var/lib/akkoma/uploads \ /var/lib/akkoma/static \ /var/lib/akkoma/config && \ chown -R akkoma:akkoma /var/lib/akkoma
# Switch to akkoma userUSER akkoma
# Expose portEXPOSE 4000
# Set environment variablesENV MIX_ENV=prodENV PHX_SERVER=trueENV PORT=4000
# Health checkHEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ CMD curl -f http://localhost:4000/api/v1/instance || exit 1
# Start the applicationCMD ["./rel/bin/akkoma", "start"]Note: This Dockerfile uses a multi-stage build to create an optimized production image. Akkoma runs on port 4000 by default, which will be your internal port in Klutch.sh. The application requires ImageMagick, FFmpeg, and ExifTool for media processing.
Step 4: Create Configuration Files
Create a production configuration file. Here’s a basic example:
config/prod.secret.exs.example:
# Copy this to config/prod.secret.exs and fill in your values# DO NOT commit prod.secret.exs to version control
import Config
# Database configurationconfig :akkoma, Pleroma.Repo, hostname: System.get_env("DB_HOST", "localhost"), port: String.to_integer(System.get_env("DB_PORT", "5432")), database: System.get_env("DB_NAME", "akkoma"), username: System.get_env("DB_USER", "akkoma"), password: System.get_env("DB_PASSWORD", ""), pool_size: 10
# Instance configurationconfig :akkoma, :instance, name: System.get_env("AKKOMA_INSTANCE_NAME", "Akkoma"), email: System.get_env("AKKOMA_EMAIL", "admin@example.com"), description: System.get_env("AKKOMA_DESCRIPTION", "Akkoma instance"), registrations_open: System.get_env("AKKOMA_REGISTRATIONS_OPEN", "false") == "true", federating: System.get_env("AKKOMA_FEDERATING", "true") == "true"
# Web endpoint configurationconfig :akkoma, Pleroma.Web.Endpoint, url: [ host: System.get_env("AKKOMA_HOST", "example-app.klutch.sh"), scheme: "https", port: 443 ], http: [ ip: {0, 0, 0, 0}, port: String.to_integer(System.get_env("PORT", "4000")) ], secret_key_base: System.get_env("SECRET_KEY_BASE"), signing_salt: System.get_env("SIGNING_SALT")
# Media uploadsconfig :akkoma, Pleroma.Upload, uploads: "/var/lib/akkoma/uploads"
# Static filesconfig :akkoma, Pleroma.Web.Endpoint, static_url: [ host: System.get_env("AKKOMA_HOST", "example-app.klutch.sh"), scheme: "https", port: 443 ]
# Email configuration (optional)config :akkoma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.SMTP, relay: System.get_env("SMTP_RELAY"), username: System.get_env("SMTP_USERNAME"), password: System.get_env("SMTP_PASSWORD"), port: String.to_integer(System.get_env("SMTP_PORT", "587")), ssl: System.get_env("SMTP_SSL", "true") == "true", tls: :always, auth: :alwaysStep 5: Create Environment Configuration Template
Create a .env.example file with required environment variables:
# Database ConfigurationDB_HOST=your-postgresql-hostDB_PORT=5432DB_NAME=akkomaDB_USER=akkomaDB_PASSWORD=your-secure-password
# Application ConfigurationAKKOMA_INSTANCE_NAME=My Akkoma InstanceAKKOMA_EMAIL=admin@example.comAKKOMA_DESCRIPTION=A federated social networking serverAKKOMA_HOST=example-app.klutch.shAKKOMA_REGISTRATIONS_OPEN=falseAKKOMA_FEDERATING=true
# SecuritySECRET_KEY_BASE=your-secret-key-base-hereSIGNING_SALT=your-signing-salt-here
# Server ConfigurationPORT=4000MIX_ENV=prodPHX_SERVER=true
# Email Configuration (Optional)SMTP_RELAY=smtp.example.comSMTP_USERNAME=your-smtp-usernameSMTP_PASSWORD=your-smtp-passwordSMTP_PORT=587SMTP_SSL=true
# TimezoneTZ=UTCStep 6: Create Database Initialization Script
Create a script to initialize the database schema:
scripts/init_db.sh:
#!/bin/bashset -e
echo "Initializing Akkoma database..."
# Wait for PostgreSQL to be readyuntil PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d postgres -c '\q'; do >&2 echo "PostgreSQL is unavailable - sleeping" sleep 1done
echo "PostgreSQL is ready"
# Create database if it doesn't existPGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d postgres <<-EOSQL SELECT 'CREATE DATABASE $DB_NAME' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '$DB_NAME')\gexecEOSQL
# Run migrationsecho "Running database migrations..."./rel/bin/akkoma eval "Akkoma.Release.migrate"
echo "Database initialization complete"Step 7: Create .dockerignore File
Create a .dockerignore file to exclude unnecessary files from the Docker build:
.git.gitignore.dockerignore.env.env.local*.mddocker-compose.ymldocker-compose.*.ymlDockerfilenode_modules_builddepspriv/staticStep 8: Test Locally (Optional)
Before deploying to Klutch.sh, you can test your Akkoma setup locally:
# Build the Docker imagedocker build -t my-akkoma .
# Run the container (assuming you have a PostgreSQL database running)docker run -d \ --name akkoma-test \ -p 4000:4000 \ -e DB_HOST=host.docker.internal \ -e DB_PORT=5432 \ -e DB_NAME=akkoma \ -e DB_USER=akkoma \ -e DB_PASSWORD=password \ -e SECRET_KEY_BASE=$(openssl rand -base64 64) \ -e SIGNING_SALT=$(openssl rand -base64 32) \ -e AKKOMA_HOST=localhost \ -v $(pwd)/uploads:/var/lib/akkoma/uploads \ my-akkoma
# Check if the application is runningcurl http://localhost:4000/api/v1/instanceNote: For local development with a database, you can use Docker Compose to run both Akkoma and PostgreSQL together. Docker Compose is only for local development; Klutch.sh does not support Docker Compose for deployment.
Step 9: Push to GitHub
Commit your Akkoma project files to your GitHub repository:
git add .git commit -m "Initial Akkoma Docker setup for Klutch.sh"git branch -M maingit remote add origin https://github.com/yourusername/akkoma-klutch.gitgit push -u origin mainDeploying to Klutch.sh
Now that your Akkoma project is ready and pushed to GitHub, follow these steps to deploy it on Klutch.sh with persistent storage.
Deployment Steps
-
Log in to Klutch.sh
Navigate to klutch.sh/app and sign in to your account.
-
Create a New Project
Go to Create Project and give your project a meaningful name (e.g., “Akkoma Social Network”).
-
Create a New App
Navigate to Create App and configure the following settings:
-
Select Your Repository
- Choose GitHub as your Git source
- Select the repository containing your Dockerfile
- Choose the branch you want to deploy (usually
mainormaster)
Klutch.sh will automatically detect the Dockerfile in your repository root and use it for deployment.
-
Configure Traffic Type
- Traffic Type: Select HTTP (Akkoma is a web application)
- Internal Port: Set to
4000(the port your Akkoma container listens on, as defined in your Dockerfile)
-
Set Environment Variables
Add the following environment variables for your Akkoma configuration:
Database Configuration:
DB_HOST: Your database host (if using a Klutch.sh PostgreSQL app, use the app URL likeexample-db.klutch.sh)DB_PORT: Database port (for Klutch.sh TCP apps, use8000externally, but the internal port in your database app should be5432for PostgreSQL)DB_NAME: Your database name (e.g.,akkoma)DB_USER: Database usernameDB_PASSWORD: Database password
Application Configuration:
AKKOMA_INSTANCE_NAME: Your instance name (e.g.,My Akkoma Instance)AKKOMA_EMAIL: Admin email addressAKKOMA_DESCRIPTION: Instance descriptionAKKOMA_HOST: Your Klutch.sh app URL (e.g.,example-app.klutch.sh)AKKOMA_REGISTRATIONS_OPEN: Set totrueorfalseto control user registrationsAKKOMA_FEDERATING: Set totrueto enable federation with other instances
Security:
SECRET_KEY_BASE: Generate usingopenssl rand -base64 64(a long random string)SIGNING_SALT: Generate usingopenssl rand -base64 32(a random string)
Server Configuration:
PORT: Set to4000(matches the internal port)MIX_ENV: Set toproductionPHX_SERVER: Set totrue
Optional - Email Configuration:
SMTP_RELAY: Your SMTP server hostnameSMTP_USERNAME: SMTP usernameSMTP_PASSWORD: SMTP passwordSMTP_PORT: SMTP port (typically587or465)SMTP_SSL: Set totrueorfalse
Timezone:
TZ: Your timezone (e.g.,UTCorAmerica/New_York)
-
Attach Persistent Volumes
Akkoma requires persistent storage for several directories to ensure data persists across deployments:
Uploads Volume:
- Mount Path:
/var/lib/akkoma/uploads - Size: Start with 20GB minimum (50GB+ recommended for production with media uploads)
This volume stores:
- User-uploaded images and media files
- Avatar images
- Background images
- Other user-generated content
Static Files Volume (Optional):
- Mount Path:
/var/lib/akkoma/static - Size: 5GB (for static assets and compiled frontend files)
Config Volume (Optional):
- Mount Path:
/var/lib/akkoma/config - Size: 1GB (for configuration files and secrets)
Note: For production instances with active users, allocate sufficient storage for media uploads. You can increase volume sizes later if needed.
- Mount Path:
-
Configure Additional Settings
- Region: Select the region closest to your users for optimal performance
- Compute Resources: Akkoma can be resource-intensive; allocate at least:
- CPU: 2+ cores recommended
- Memory: 2GB minimum (4GB+ recommended for production workloads)
- Instances: Start with 1 instance (you can scale horizontally later if needed)
-
Deploy Your Application
Click “Create” to start the deployment. Klutch.sh will:
- Automatically detect your Dockerfile in the repository root
- Build the Docker image
- Attach the persistent volume(s)
- Start your Akkoma container
- Assign a URL for external access
Note: The first deployment may take several minutes as it builds the Docker image, compiles the Elixir application, and installs dependencies.
-
Initialize Database
After deployment, you’ll need to initialize your Akkoma database. Connect to your PostgreSQL database and run the database migrations. You can do this by executing commands in your container or using a database migration tool.
-
Create Admin User
Once the database is initialized, create your first admin user. You can do this through the web interface after accessing your instance, or by using the Akkoma CLI commands if available in your deployment.
-
Access Your Application
Once deployment is complete, you’ll receive a URL like
example-app.klutch.sh. Visit this URL to access your Akkoma instance and complete the setup.
Sample Code: Getting Started with Akkoma
Here are some examples to help you interact with your Akkoma instance:
Example 1: JavaScript Client - Fetching Instance Information
// Frontend JavaScript example for Akkoma API
async function getInstanceInfo() { try { const response = await fetch('https://example-app.klutch.sh/api/v1/instance', { method: 'GET', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' } });
if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); }
const instanceInfo = await response.json(); console.log('Instance Info:', instanceInfo); return instanceInfo; } catch (error) { console.error('Error fetching instance info:', error); throw error; }}
// Display instance informationasync function displayInstanceInfo() { const info = await getInstanceInfo(); document.getElementById('instance-name').textContent = info.title; document.getElementById('instance-description').textContent = info.description; document.getElementById('instance-version').textContent = info.version;}Example 2: Fetching Public Timeline
async function getPublicTimeline() { try { const response = await fetch('https://example-app.klutch.sh/api/v1/timelines/public?limit=20', { method: 'GET', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' } });
if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); }
const posts = await response.json(); console.log('Public Timeline:', posts); return posts; } catch (error) { console.error('Error fetching timeline:', error); throw error; }}
// Display timeline postsasync function displayTimeline() { const posts = await getPublicTimeline(); const container = document.getElementById('timeline-container');
posts.forEach(post => { const postElement = document.createElement('div'); postElement.className = 'post'; postElement.innerHTML = ` <div class="post-author">@${post.account.acct}</div> <div class="post-content">${post.content}</div> <div class="post-date">${new Date(post.created_at).toLocaleString()}</div> `; container.appendChild(postElement); });}Example 3: Authenticated API Request
async function getAuthenticatedTimeline(accessToken) { try { const response = await fetch('https://example-app.klutch.sh/api/v1/timelines/home', { method: 'GET', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'Authorization': `Bearer ${accessToken}` } });
if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); }
const posts = await response.json(); return posts; } catch (error) { console.error('Error fetching home timeline:', error); throw error; }}Example 4: Python Client Example
import requestsimport json
class AkkomaClient: def __init__(self, base_url, access_token=None): self.base_url = base_url self.access_token = access_token self.headers = { 'Content-Type': 'application/json', 'Accept': 'application/json' } if access_token: self.headers['Authorization'] = f'Bearer {access_token}'
def get_instance_info(self): """Get instance information""" response = requests.get( f'{self.base_url}/api/v1/instance', headers=self.headers ) response.raise_for_status() return response.json()
def get_public_timeline(self, limit=20): """Get public timeline""" response = requests.get( f'{self.base_url}/api/v1/timelines/public', headers=self.headers, params={'limit': limit} ) response.raise_for_status() return response.json()
def get_home_timeline(self, limit=20): """Get home timeline (requires authentication)""" if not self.access_token: raise ValueError("Access token required for home timeline")
response = requests.get( f'{self.base_url}/api/v1/timelines/home', headers=self.headers, params={'limit': limit} ) response.raise_for_status() return response.json()
# Example usageclient = AkkomaClient('https://example-app.klutch.sh')
# Get instance infoinfo = client.get_instance_info()print(f"Instance: {info['title']}")print(f"Version: {info['version']}")
# Get public timelinetimeline = client.get_public_timeline(limit=10)print(f"Found {len(timeline)} posts in public timeline")Production Best Practices
Security Recommendations
- Enable HTTPS: Always use HTTPS in production (Klutch.sh provides TLS certificates)
- Secure Environment Variables: Store all sensitive credentials as environment variables in Klutch.sh
- Strong Secrets: Generate strong
SECRET_KEY_BASEandSIGNING_SALTvalues using secure random generators - Database Security: Use strong database passwords and enable SSL connections
- Registration Control: Carefully manage
AKKOMA_REGISTRATIONS_OPENto prevent spam - Rate Limiting: Configure rate limiting to prevent abuse
- Content Security Policy: Implement CSP headers for additional security
- Regular Updates: Keep Akkoma and dependencies updated with security patches
- Backup Strategy: Regularly backup your database and uploads volume
- Moderation: Set up proper moderation tools and policies for your instance
Performance Optimization
- Database Optimization: Regularly optimize PostgreSQL database with VACUUM and ANALYZE
- Media Processing: Optimize ImageMagick and FFmpeg settings for your workload
- Caching: Implement caching strategies for frequently accessed data
- CDN Integration: Consider using a CDN for static assets and media files
- Connection Pooling: Configure appropriate database connection pool sizes
- Resource Monitoring: Monitor CPU, memory, and storage usage
- Media Storage: Consider external object storage for media files at scale
Federation Best Practices
- Instance Reputation: Maintain a good reputation in the Fediverse by following community guidelines
- Content Moderation: Implement effective content moderation policies
- Block Lists: Consider using community block lists to protect your users
- Rate Limiting: Implement rate limiting to prevent abuse
- Documentation: Provide clear documentation for your instance rules and policies
- Community Guidelines: Establish and enforce community guidelines
Monitoring and Maintenance
Monitor your Akkoma application for:
- Application Logs: Check logs in Klutch.sh dashboard for errors
- Database Performance: Monitor query performance and slow queries
- Storage Usage: Monitor persistent volume usage and plan for growth
- Response Times: Track API response times
- Error Rates: Monitor 4xx and 5xx error rates
- Resource Usage: Track CPU and memory usage in Klutch.sh dashboard
- Federation Health: Monitor federation activity and connection status
Regular maintenance tasks:
- Backup Database: Regularly backup your PostgreSQL database
- Backup Media Files: Backup uploaded media files from the persistent volume
- Update Dependencies: Keep Elixir dependencies updated
- Review Logs: Review application and error logs regularly
- Security Audits: Perform regular security audits
- Database Maintenance: Regularly run database maintenance tasks
Troubleshooting
Application Not Loading
- Verify the app’s Traffic Type is HTTP
- Check that the internal port is set to
4000and matches your Dockerfile - Review build and runtime logs in the Klutch.sh dashboard
- Ensure the Elixir application starts correctly (check the CMD in Dockerfile)
- Verify all required environment variables are set
Database Connection Issues
- Verify database environment variables are set correctly
- For Klutch.sh PostgreSQL apps, use the app URL as the host and port
8000externally - Check that the database is accessible from your Akkoma app
- Verify database credentials and permissions
- Ensure the database schema has been initialized with migrations
Media Upload Issues
- Ensure persistent volume is mounted at
/var/lib/akkoma/uploads - Check file permissions on the uploads directory
- Verify ImageMagick, FFmpeg, and ExifTool are installed and working
- Ensure sufficient disk space in the persistent volume
- Check media processing configuration
Federation Issues
- Verify
AKKOMA_FEDERATINGis set totrue - Check that your instance URL is correctly configured
- Ensure your domain DNS is properly configured
- Verify ActivityPub endpoints are accessible
- Check federation logs for connection errors
Performance Issues
- Review database query performance and add indexes if needed
- Check resource allocation in Klutch.sh (CPU and memory)
- Monitor database connection pool usage
- Review application logs for slow operations
- Consider implementing caching for frequently accessed data
- Optimize media processing settings
Data Not Persisting
- Ensure persistent volumes are correctly mounted
- Check file permissions on persistent volumes
- Verify the application is writing to the correct directories
- Ensure sufficient disk space in persistent volumes
Related Documentation
- Learn more about deploying applications on Klutch.sh in Deployments
- Understand traffic types, ports, and routing in Networking
- Explore how to work with storage in Volumes
- Browse the full platform documentation at Klutch.sh Documentation
- For Akkoma-specific details, see the official Akkoma Documentation
- Learn about ActivityPub and federation at ActivityPub Specification
Conclusion
Deploying Akkoma to Klutch.sh with a Dockerfile provides a scalable, reliable federated social networking solution with persistent storage, automatic deployments, and production-ready configuration. By following this guide, you’ve set up a high-performance Akkoma instance with proper data persistence, security configurations, and the ability to participate in the Fediverse.
Akkoma’s lightweight architecture, privacy-focused design, and comprehensive federation support make it an excellent choice for hosting your own social networking instance. Your application is now ready to connect with users across the Fediverse, share content, and build communities while maintaining control over your data and infrastructure.
Remember to follow the production best practices outlined in this guide, regularly monitor your application performance, and adjust resources as your user base grows. With proper configuration, monitoring, and maintenance, Akkoma on Klutch.sh will provide a reliable, secure foundation for your federated social networking needs.