Fn Project Serverless Platform Installation
Fn Project is a container-native, cloud-agnostic serverless platform built on Docker that lets you run functions in any language on Linux. This guide walks through installing the Fn server, developing functions with hot containers, configuring triggers, and deploying to production with best practices.
Prerequisites
- Ubuntu 20.04/22.04 or CentOS 8/Rocky Linux 8+
- Docker 20.10+ installed and running
- At least 2 GB RAM and 2 CPUs
curlinstalled- A Docker registry (Docker Hub or private) for production deployments
Install the Fn Server
Fn Server runs as a Docker container:
# Start Fn server (pulls the image automatically)
docker run --rm -it \
-v /var/run/docker.sock:/var/run/docker.sock \
--name fnserver \
-p 8080:8080 \
-d \
fnproject/fnserver:latest
# Verify it's running
docker ps | grep fnserver
curl http://localhost:8080/version
For persistent data, mount a volume:
# Create a data directory
mkdir -p /opt/fn/data
# Run with persistent storage
docker run --rm -it \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /opt/fn/data:/app/data \
--name fnserver \
-p 8080:8080 \
-d \
fnproject/fnserver:latest
Install the Fn CLI
# Install using the official install script
curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh
# Verify the installation
fn version
# Configure the Fn CLI to connect to your server
export FN_API_URL=http://localhost:8080
# Or set it persistently
echo 'export FN_API_URL=http://localhost:8080' >> ~/.bashrc
source ~/.bashrc
Create Your First Function
Fn uses application groupings (apps) to organize functions:
# Create an application
fn create app myapp
# List apps
fn list apps
# Initialize a new function (choose a runtime)
mkdir ~/myfunc && cd ~/myfunc
fn init --runtime node myfunction
# The init creates:
# func.yaml - function metadata
# func.js - function handler
# package.json
# View the generated function
cat func.js
Fn generates a basic handler. Customize it:
cat > func.js << 'EOF'
const fdk = require('@fnproject/fdk');
fdk.handle(function(input) {
let name = 'World';
if (input && input.name) {
name = input.name;
}
return { message: `Hello from Fn, ${name}!` };
});
EOF
# Deploy the function to your app
fn deploy --app myapp --local
# List functions in the app
fn list functions myapp
# Invoke the function
fn invoke myapp myfunction
echo '{"name": "VPS User"}' | fn invoke myapp myfunction
Create a Python function:
mkdir ~/pyfunc && cd ~/pyfunc
fn init --runtime python mypyfunc
cat > func.py << 'EOF'
import fdk
import json
import datetime
def handler(ctx, data=None, loop=None):
body = json.loads(data.getvalue())
name = body.get("name", "World")
return {"greeting": f"Hello {name}", "time": datetime.datetime.utcnow().isoformat()}
if __name__ == "__main__":
fdk.handle(handler)
EOF
fn deploy --app myapp --local
echo '{"name":"Python"}' | fn invoke myapp mypyfunc
Hot Containers and Performance
Hot containers keep function containers warm between invocations, dramatically reducing cold start latency:
# Configure hot containers in func.yaml
cat func.yaml
# name: myfunction
# version: 0.0.2
# runtime: node
# Update func.yaml to enable hot container settings
cat > func.yaml << 'EOF'
schema_version: 20180708
name: myfunction
version: 0.0.3
runtime: node
entrypoint: node func.js
format: http-stream
EOF
# Set idle timeout for hot containers (seconds)
fn update function myapp myfunction --idle-timeout 30
# Set concurrency (how many requests per container)
fn update function myapp myfunction --max-concurrency 5
# Check function config
fn inspect function myapp myfunction
Benchmark cold vs hot starts:
# Run 10 concurrent invocations
for i in {1..10}; do
fn invoke myapp myfunction &
done
wait
# Check Fn server logs for timing info
docker logs fnserver --tail 50
Triggers and Scheduled Calls
Fn supports HTTP triggers to expose functions as web endpoints:
# Create an HTTP trigger
fn create trigger myapp myfunction mytrigger \
--type http \
--source /hello
# List triggers
fn list triggers myapp
# The trigger URL format:
# http://localhost:8080/t/myapp/hello
# Test via HTTP
curl -X POST http://localhost:8080/t/myapp/hello \
-H "Content-Type: application/json" \
-d '{"name": "HTTP Trigger"}'
For scheduled (cron) triggers, use the Fn Flow or an external scheduler:
# Use cron to call the trigger on a schedule
# Add to crontab: crontab -e
# */5 * * * * curl -s -X POST http://localhost:8080/t/myapp/hello -d '{}'
crontab -l | { cat; echo "*/5 * * * * curl -s -X POST http://localhost:8080/t/myapp/hello -d '{}'"; } | crontab -
Flow Orchestration
Fn Flow provides a programmatic way to compose functions:
# Start the Fn Flow server
docker run --rm \
-p 8081:8081 \
--name flowserver \
-d \
-e API_URL=http://localhost:8080 \
fnproject/flow:latest
# Create a flow function
mkdir ~/flowfunc && cd ~/flowfunc
fn init --runtime java flowfunc
# Edit the Java handler to use flows
# (Flows are primarily used in Java and Go runtimes)
# The FlowFuture API allows chaining async function calls
Production Deployment
For production, run Fn with a MySQL or PostgreSQL backend and configure resource limits:
# Create a docker-compose.yml for production
cat > /opt/fn/docker-compose.yml << 'EOF'
version: '3.7'
services:
fnserver:
image: fnproject/fnserver:latest
ports:
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- fndata:/app/data
environment:
- DB_URL=sqlite3:///app/data/fn.db
- MQ_URL=bolt:///app/data/fn.mq
- FN_LOG_LEVEL=info
restart: unless-stopped
deploy:
resources:
limits:
memory: 2G
volumes:
fndata:
EOF
cd /opt/fn && docker-compose up -d
# Configure Nginx as a reverse proxy
cat > /etc/nginx/sites-available/fn << 'EOF'
server {
listen 80;
server_name fn.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 300s;
}
}
EOF
ln -s /etc/nginx/sites-available/fn /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
Push functions to a Docker registry:
# Log in to your registry
docker login
# Deploy to production (without --local flag)
export FN_REGISTRY=your-dockerhub-username
fn deploy --app myapp
Troubleshooting
Function invocation returns 500 or times out:
# Check server logs
docker logs fnserver --tail 100
# Increase function timeout (ms)
fn update function myapp myfunction --timeout 120
# Check Docker daemon can pull images
docker pull fnproject/node:latest
"Docker socket permission denied" error:
# Add current user to the docker group
sudo usermod -aG docker $USER
newgrp docker
# Or fix socket permissions
sudo chmod 666 /var/run/docker.sock
Function stuck in building state:
# Manually build the Docker image
fn build
# Check build logs
fn build --verbose
CLI cannot reach Fn server:
# Verify FN_API_URL is set
echo $FN_API_URL
# Test connectivity
curl http://localhost:8080/version
# Check server is listening
ss -tlnp | grep 8080
Conclusion
Fn Project delivers a lightweight, Docker-native serverless platform that runs entirely on your own VPS or baremetal server. Hot containers minimize cold-start latency, HTTP triggers expose functions via REST, and the Flow API enables complex multi-step orchestrations. Deploy with Docker Compose for a simple production setup, and use a private registry to manage versioned function images.


