Architecture
Surfly uses a pluggable architecture built on systemd quadlets and podman containers. This design provides flexibility for customization while maintaining deployment consistency and reliability.
Systemd Quadlets Overview
Section titled “Systemd Quadlets Overview”Surfly services are deployed as systemd quadlets - a modern approach that combines the benefits of containerization with systemd service management. Each service is defined by a .container file that specifies:
- Container image and version
- Volume mounts and networking
- Environment configuration
- Service dependencies and restart policies
Example: HAProxy Service Definition
Section titled “Example: HAProxy Service Definition”Here’s the quadlet configuration for the HAProxy service:
[Unit]Description=Reverse proxy server for SurflyPartOf=ss-core.target
[Install]WantedBy=multi-user.target default.target
[Service]SyslogIdentifier=ss-haproxyRestart=on-failureSuccessExitStatus=143TimeoutStopSec=70
[Container]ContainerName=ss-haproxyLogDriver=journaldNetwork=hostRunInit=trueImage=registry.surfly.com/surfly/ss-haproxy-production:c24017551b64b07021e2fa7c0055c14399af7f61Volume=%h/surfly:/surflyVolume=%h/surfly/certs:/certsEnvironmentFile=%h/surfly/config.envEnvironmentFile=%h/surfly/license.envExec=haproxy -Ws -f /app/haproxy.cfg -p /run/haproxy.pidService Generation Process
Section titled “Service Generation Process”Systemd automatically converts quadlet .container files into standard systemd service files using the podman-user-generator. You can view the generated service file to understand how your quadlet configuration translates to systemd directives:
systemctl --user cat ss-haproxyThis shows the generated service file with additional systemd-specific configurations:
# Automatically generated by /usr/lib/systemd/user-generators/podman-user-generator
[Unit]Wants=podman-user-wait-network-online.serviceAfter=podman-user-wait-network-online.serviceDescription=Reverse proxy server for SurflyPartOf=ss-core.targetSourcePath=/home/client/.config/containers/systemd/ss-haproxy.containerRequiresMountsFor=%t/containersRequiresMountsFor=/etc/pki/ca-trust/extracted
[Install]WantedBy=multi-user.target default.target
[Service]SyslogIdentifier=ss-haproxyRestart=on-failureSuccessExitStatus=143TimeoutStopSec=70Environment=PODMAN_SYSTEMD_UNIT=%nKillMode=mixedExecStop=/usr/bin/podman rm -v -f -i --cidfile=%t/%N.cidExecStopPost=-/usr/bin/podman rm -v -f -i --cidfile=%t/%N.cidDelegate=yesType=notifyNotifyAccess=allExecStart=/usr/bin/podman run --name ss-haproxy --cidfile=%t/%N.cid --replace --rm --log-driver journald --cgroups=split --init --network host --sdnotify=conmon -d -v %h/surfly/certs:/certs -v /etc/pki/ca-trust/extracted:/etc/pki/ca-trust/extracted --env NGINX_DNS_RESOLVERS=1.1.1.1 --env SENTRY_ENVIRONMENT=stag-dev-cobro-eufi --env-file %h/surfly/config.env --env-file %h/surfly/license.env registry.surfly.com/surfly/ss-haproxy-production:c24017551b64b07021e2fa7c0055c14399af7f61 haproxy -Ws -f /app/haproxy.cfg -p /run/haproxy.pid
[X-Container]ContainerName=ss-haproxyLogDriver=journaldNetwork=hostRunInit=trueImage=registry.surfly.com/surfly/ss-haproxy-production:c24017551b64b07021e2fa7c0055c14399af7f61Volume=%h/surfly:/surflyVolume=%h/surfly/certs:/certsEnvironmentFile=%h/surfly/config.envEnvironmentFile=%h/surfly/license.envExec=haproxy -Ws -f /app/haproxy.cfg -p /run/haproxy.pidEnvironment="NGINX_DNS_RESOLVERS=1.1.1.1"Environment="SENTRY_ENVIRONMENT=stag-dev-cobro-eufi"This generated service file represents the final configuration after applying all overrides and merging all environment sources.
You can also generate all services in dry-run mode by running:
/usr/lib/systemd/system-generators/podman-system-generator --user --dryrunCustomization Through Overrides
Section titled “Customization Through Overrides”Service Overrides
Section titled “Service Overrides”Create systemd service overrides to customize any aspect of a service:
# Create override directorymkdir -p ~/.config/systemd/user/ss-haproxy.service.d/
# Create override filecat > ~/.config/systemd/user/ss-haproxy.service.d/custom.conf << EOF[Container]# Add custom volume mountVolume=%h/custom-config:/custom-config:ro
# Add environment variableEnvironment=CUSTOM_SETTING=valueEOF
# Reload and restartsystemctl --user daemon-reloadsystemctl --user restart ss-haproxyFor more detailed information about available override options, see the podman-systemd.unit documentation.
Deployment Flexibility
Section titled “Deployment Flexibility”Surfly supports systemd quadlets as the official deployment method. However, since all services are containerized microservices, it is possible to migrate to other deployment methods by converting the quadlet definitions to other formats.
Docker Compose
Section titled “Docker Compose”Example converting quadlet definitions to Docker Compose format:
services: haproxy: image: registry.surfly.com/surfly/ss-haproxy-production:latest network_mode: host volumes: - ~/surfly:/surfly - ~/surfly/certs:/certs env_file: - ~/surfly/config.env - ~/surfly/license.env command: haproxy -Ws -f /app/haproxy.cfg -p /run/haproxy.pid restart: on-failureKubernetes
Section titled “Kubernetes”Example converting quadlet definitions to Kubernetes deployments with appropriate manifests:
apiVersion: apps/v1kind: Deploymentmetadata: name: ss-haproxyspec: replicas: 1 selector: matchLabels: app: ss-haproxy template: spec: containers: - name: haproxy image: registry.surfly.com/surfly/ss-haproxy-production:latest command: ["haproxy", "-Ws", "-f", "/app/haproxy.cfg"] volumeMounts: - name: surfly-config mountPath: /surfly envFrom: - configMapRef: name: surfly-config