# Deployment

This guide explains how to deploy applications using the Falcon web server. It covers the recommended deployment methods, configuration options, and examples for different environments, including systemd and kubernetes.

Falcon can be deployed into production either as a standalone application server, or as a virtual host routing to multiple applications. Both configurations can run behind a load balancer, but `falcon virtual` is designed to be zero-configuration deployment option.

## Falcon Serve

`falcon serve` is not designed for deployment because the command line interface is not guaranteed to be stable nor does it expose every possible configuration option.

## Falcon Hosts

`falcon host` is designed for deployment, and is the recommended way to deploy Falcon in production. It exposes a well defined interface for configuring services (web applications, job servers, etc).

### Configuration

`falcon host` loads configuration from the `falcon.rb` file in your application directory. This file contains configuration blocks which define how to host the application and any related services. This file should be executable and it invokes `falcon-host` which starts all defined services.

Here is a basic example which hosts a rack application:

~~~ ruby
#!/usr/bin/env falcon-host
# frozen_string_literal: true

require "falcon/environment/rack"
require "falcon/environment/lets_encrypt_tls"
require "async/service/supervisor"

hostname = File.basename(__dir__)
service hostname do
	include Falcon::Environment::Rack
	include Falcon::Environment::LetsEncryptTLS

	# Insert an in-memory cache in front of the application (using async-http-cache).
	cache true
	
	# Connect to the supervisor for monitoring.
	include Async::Service::Supervisor::Supervised
end

service "supervisor" do
	include Async::Service::Supervisor::Environment
	
	monitors do
		[
			MemoryMonitor.new(
				# Check every 10 seconds:
				interval: 10,
				# Per-supervisor (cluster) limit:
				total_size_limit: 1000*1024*1024,
				# Per-process limit:
				maximum_size_limit: 200*1024*1024
			)
		]
	end
end
~~~

These configuration blocks are evaluated using the [async-service](https://github.com/socketry/async-service) gem. This configuration will bind Falcon to an [IPC socket](https://en.wikipedia.org/wiki/Unix_domain_socket) and is designed to be used with a reverse proxy such as [`falcon virtual`](#falcon-virtual).

The supervisor is an independent service which monitors the health of the application and can restart it if necessary. Other services like background job processors can be added to the configuration.

### Environments

The service blocks define configuration that is loaded by the service layer to control how the service is run. The `service ... do` block defines the service name and the environment in which it runs. Different modules can be included to provide different functionality, such as `Falcon::Environment::Rack` for Rack applications, or `Falcon::Environment::LetsEncryptTLS` for automatic TLS certificate management.

**NOTE**: Falcon does not provision or renew certificates automatically. Use a tool like [certbot](https://certbot.eff.org) to provision your certificate and the `LetsEncryptTLS` environment will automatically read it in.

### Application Configuration

The environment configuration is defined in the `Falcon::Environment` module. The {ruby Falcon::Environment::Application} environment supports the generic virtual host functionality, but you can customise any parts of the configuration, e.g. to bind a production host to `localhost:3000` using plaintext HTTP/2:

~~~ ruby
#!/usr/bin/env falcon-host
# frozen_string_literal: true

require "falcon/environment/rack"
require "async/service/supervisor"

hostname = File.basename(__dir__)
service hostname do
	include Falcon::Environment::Rack

	endpoint do
		Async::HTTP::Endpoint
			.parse('http://localhost:3000')
			.with(
				protocol: Async::HTTP::Protocol::HTTP2
			)
	end
end

service "supervisor" do
	include Async::Service::Supervisor::Environment
end
~~~

You can verify this is working using the [`nghttp2` client](https://nghttp2.org): `nghttp -v http://localhost:3000`. This will not work in a browser as they mandate TLS for HTTP/2 connections.

#### Self-signed certificate

You can use a self-signed certificate to test your server configuration locally. First, provision a certificate using the [`localhost` gem](https://github.com/socketry/localhost):

```
$ bundle exec bake localhost:install
```

You may be prompted for a password to install the certificate. This is the password for your local keychain.

Then, add the `SelfSignedTLS` environment to your configuration and set up the SSL context:

~~~ ruby
#!/usr/bin/env falcon-host
# frozen_string_literal: true

require "falcon/environment/rack"
require "async/service/supervisor"

hostname = File.basename(__dir__)
service hostname do
	include Falcon::Environment::Rack
	include Falcon::Environment::SelfSignedTLS

	endpoint do
		Async::HTTP::Endpoint
			.parse('https://localhost:3000')
			.with(
				protocol: Async::HTTP::Protocol::HTTP2,
				ssl_context: ssl_context
			)
	end
end

service "supervisor" do
	include Async::Service::Supervisor::Environment
end
~~~

You should now be able to access your server at `https://localhost:3000` in your browser.

#### Application Configuration Example for Heroku

Building on the examples above, the following is a full configuration example for Heroku:

~~~ bash
# Procfile

web: bundle exec falcon host
~~~

~~~ ruby
# falcon.rb

#!/usr/bin/env -S falcon host
# frozen_string_literal: true

require "falcon/environment/rack"

hostname = File.basename(__dir__)

service hostname do
	include Falcon::Environment::Rack
	
	# By default, Falcon uses Etc.nprocessors to set the count, which is likely incorrect on shared hosts like Heroku.
	# Review the following for guidance about how to find the right value for your app:
	# https://help.heroku.com/88G3XLA6/what-is-an-acceptable-amount-of-dyno-load
	# https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#workers
	count ENV.fetch("WEB_CONCURRENCY", 1).to_i
	
	# If using count > 1 you may want to preload your app to reduce memory usage and increase performance:
	preload "preload.rb"
	
	# The default port should be 3000, but you can change it to match your Heroku configuration.
	port {ENV.fetch("PORT", 3000).to_i}
	
	# Heroku only supports HTTP/1.1 at the time of this writing. Review the following for possible updates in the future:
	# https://devcenter.heroku.com/articles/http-routing#http-versions-supported
	# https://github.com/heroku/roadmap/issues/34
	endpoint do
		Async::HTTP::Endpoint
			.parse("http://0.0.0.0:#{port}")
			.with(protocol: Async::HTTP::Protocol::HTTP11)
	end
~~~

~~~ ruby
# preload.rb

# frozen_string_literal: true

require_relative "config/environment"
~~~

### Kubernetes Integration

Falcon can be deployed in a Kubernetes cluster using the `falcon host` command. You can use the following example to create a Kubernetes deployment for Falcon:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: falcon
spec:
  replicas: 1
  selector:
    matchLabels:
      app: falcon
  template:
    metadata:
      labels:
        app: falcon
    spec:
      containers:
        - name: my-app
          image: my-app-image:latest
          env:
            - name: NOTIFY_LOG
              value: "/tmp/notify.log"
          ports:
            - containerPort: 9292
          readinessProbe:
            exec:
              command: ["bundle", "exec", "bake", "async:container:notify:log:ready?"]
            initialDelaySeconds: 5
            periodSeconds: 5
            failureThreshold: 12

          # Assuming you are running Rails and have bound to port 3000:
          livenessProbe:
            httpGet:
              path: /up
              port: 3000
            initialDelaySeconds: 10
            periodSeconds: 10
            failureThreshold: 3
```

## Falcon Virtual

Falcon virtual provides a virtual host proxy and HTTP-to-HTTPS redirection for multiple applications. It is designed to be a zero-configuration deployment option, allowing you to run multiple applications on the same server.

~~~ mermaid
graph TD;
	client[Client Browser] -->|TLS + HTTP/2 TCP| proxy["Falcon Proxy (SNI)"];
	proxy -->|HTTP/2 UNIX PIPE| server["Application Server (Rack Compatible)"];
~~~

You need to create a `falcon.rb` configuration in the root of your applications, and start the virtual host:

~~~ bash
falcon virtual /srv/http/*/falcon.rb
~~~

By default, it binds to both HTTP and HTTPS ports, and automatically redirects HTTP requests to HTTPS. It also supports TLS SNI for resolving the certificates.

See the [docker example](https://github.com/socketry/falcon-virtual-docker-example) for a complete working example.

### systemd Service File

You can create a systemd service file to manage the Falcon virtual service. Here is an example of a systemd service file for Falcon:

```ini
[Unit]
Description=Falcon Virtual Service
After=network.target

[Service]
Type=notify
User=http
WorkingDirectory=/srv/http
ExecStart=/usr/local/bin/falcon virtual /srv/http/*/falcon.rb
Restart=always

[Install]
WantedBy=multi-user.target
```
