Skip to content

Systemd Services

fscm provides utilities for creating and managing systemd services.

Installation

The systemd module is included with fscm:

from fscm.modules import systemd

Creating Simple Services

The simple_service() function creates a complete systemd service:

from fscm.modules import systemd

# Create a service that runs a command
systemd.simple_service(
    name="myapp",
    exec_start="/usr/bin/python3 /opt/myapp/app.py",
    description="My Application",
    user="www-data",
    working_dir="/opt/myapp"
)

This creates: - /etc/systemd/system/myapp.service — Service unit file - Enables the service to start on boot - Starts the service

Parameters

Parameter Type Description
name str Service name
exec_start str Command to run
description str Service description
user str User to run as
group str Group to run as
working_dir str Working directory
env_file str Environment file path
env_vars dict Environment variables
restart str Restart policy (always, on-failure)
restart_sec int Seconds before restart
after list Services to start after
wants list Soft dependencies
requires list Hard dependencies

Service with Environment Variables

from fscm.modules import systemd

systemd.simple_service(
    name="myapp",
    exec_start="/opt/myapp/venv/bin/python app.py",
    user="myapp",
    working_dir="/opt/myapp",
    env_vars={
        "DATABASE_URL": "postgresql://localhost/myapp",
        "REDIS_URL": "redis://localhost:6379",
        "DEBUG": "false"
    }
)

Service with Environment File

import fscm
from fscm.modules import systemd

# Create environment file
fscm.file(
    "/etc/myapp/env",
    """
DATABASE_URL=postgresql://localhost/myapp
REDIS_URL=redis://localhost:6379
SECRET_KEY=your-secret-key
""",
    mode="0600",
    owner="myapp:myapp"
)

# Create service using env file
systemd.simple_service(
    name="myapp",
    exec_start="/opt/myapp/venv/bin/gunicorn app:app",
    user="myapp",
    working_dir="/opt/myapp",
    env_file="/etc/myapp/env"
)

User Services

Create services that run under a user's systemd instance:

from fscm.modules import systemd

systemd.user_simple_service(
    name="myapp",
    exec_start="/home/user/myapp/run.sh",
    description="My User Application",
    user="myuser"
)

This creates the service in ~/.config/systemd/user/ and manages it with systemctl --user.

Docker Compose Services

Run docker-compose projects as systemd services:

from fscm.modules import systemd

systemd.docker_compose_service(
    name="mystack",
    compose_file="/opt/mystack/docker-compose.yml",
    description="My Docker Stack"
)

This creates a service that: - Runs docker-compose up on start - Runs docker-compose down on stop - Depends on docker.service

Parameters

Parameter Type Description
name str Service name
compose_file str Path to docker-compose.yml
description str Service description
project_name str Docker Compose project name
env_file str Environment file for compose

Managing Services Manually

For more control, create unit files directly:

import fscm

unit_content = """
[Unit]
Description=My Custom Service
After=network.target postgresql.service
Requires=postgresql.service

[Service]
Type=notify
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/venv/bin/gunicorn --workers 4 app:app
ExecReload=/bin/kill -s HUP $MAINPID
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal

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

fscm.file("/etc/systemd/system/myapp.service", unit_content, mode="0644")
fscm.run("systemctl daemon-reload", sudo=True)
fscm.run("systemctl enable myapp", sudo=True)
fscm.run("systemctl start myapp", sudo=True)

Service Control

import fscm

# Reload systemd after changes
fscm.run("systemctl daemon-reload", sudo=True)

# Enable service (start on boot)
fscm.run("systemctl enable myapp", sudo=True)

# Start service
fscm.run("systemctl start myapp", sudo=True)

# Stop service
fscm.run("systemctl stop myapp", sudo=True)

# Restart service
fscm.run("systemctl restart myapp", sudo=True)

# Check status
result = fscm.run_ro("systemctl is-active myapp")
if result.ok:
    print("Service is running")

Example: Gunicorn Service

import fscm
from fscm.modules import systemd

def setup_gunicorn_service(
    app_name: str,
    app_dir: str,
    app_module: str,
    workers: int = 4,
    user: str = "www-data"
):
    """Set up a Gunicorn WSGI service."""
    venv = f"{app_dir}/venv"

    # Create the service
    systemd.simple_service(
        name=app_name,
        exec_start=(
            f"{venv}/bin/gunicorn "
            f"--workers {workers} "
            f"--bind unix:/run/{app_name}.sock "
            f"{app_module}"
        ),
        description=f"{app_name} Gunicorn Service",
        user=user,
        group=user,
        working_dir=app_dir,
        env_file=f"/etc/{app_name}/env",
        restart="always",
        restart_sec=5
    )

# Usage
setup_gunicorn_service(
    app_name="myapp",
    app_dir="/opt/myapp",
    app_module="myapp.wsgi:application",
    workers=4
)

Example: Timer Service

Create a service that runs on a schedule:

import fscm

# Service that does the actual work
service = """
[Unit]
Description=Backup Service

[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
User=backup
"""

# Timer that triggers the service
timer = """
[Unit]
Description=Run backup daily

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target
"""

fscm.file("/etc/systemd/system/backup.service", service, mode="0644")
fscm.file("/etc/systemd/system/backup.timer", timer, mode="0644")

fscm.run("systemctl daemon-reload", sudo=True)
fscm.run("systemctl enable backup.timer", sudo=True)
fscm.run("systemctl start backup.timer", sudo=True)

Example: Socket-Activated Service

import fscm

# Socket unit
socket = """
[Unit]
Description=My App Socket

[Socket]
ListenStream=/run/myapp.sock
SocketUser=www-data
SocketGroup=www-data
SocketMode=0660

[Install]
WantedBy=sockets.target
"""

# Service unit
service = """
[Unit]
Description=My App Service
Requires=myapp.socket

[Service]
Type=notify
User=myapp
ExecStart=/opt/myapp/venv/bin/gunicorn --workers 4 -b unix:/run/myapp.sock app:app
Restart=always
"""

fscm.file("/etc/systemd/system/myapp.socket", socket, mode="0644")
fscm.file("/etc/systemd/system/myapp.service", service, mode="0644")

fscm.run("systemctl daemon-reload", sudo=True)
fscm.run("systemctl enable myapp.socket", sudo=True)
fscm.run("systemctl start myapp.socket", sudo=True)

Next Steps