Text to Speech
    CPU Friendly

    Deploy Piper TTS on a VPS

    Self-host Piper, a fast local neural text-to-speech engine, on a RamNode VPS with a FastAPI wrapper, systemd service, and nginx TLS.

    Piper is a fast, local neural text-to-speech engine designed to run well on modest CPU hardware (it was built with Raspberry Pi in mind), so it's an easy, low-resource deployment on nearly any RamNode VPS.

    1. Choose and size the VPS

    Piper is lightweight — it is one of the least demanding services you can self-host.

    Use caseRecommended RamNode plan specs
    Light/personal use, single low-quality voice1 vCPU, 1–2 GB RAM
    Regular use, multiple voices/medium quality2 vCPU, 2–4 GB RAM
    Higher throughput / concurrent requests4 vCPU, 4–8 GB RAM

    Recommended OS: Ubuntu 24.04 LTS.

    2. Initial server setup

    shell
    apt update && apt -y upgrade
    apt -y install build-essential python3 python3-venv python3-pip wget unzip ufw ca-certificates
    
    ufw allow OpenSSH
    ufw allow 5000/tcp   # the port the wrapper API will use below
    ufw enable

    Create a dedicated service user and directory layout:

    shell
    adduser --system --group --home /opt/piper piper
    mkdir -p /opt/piper/voices
    chown -R piper:piper /opt/piper

    3. Install Piper

    The simplest path on Linux x86_64 is the prebuilt release binary rather than compiling from source:

    shell
    su - piper -s /bin/bash
    cd /opt/piper
    
    # Check https://github.com/OHF-Voice/piper1-gpl/releases (or rhasspy/piper) for the latest tag
    wget https://github.com/rhasspy/piper/releases/latest/download/piper_linux_x86_64.tar.gz
    tar -xzf piper_linux_x86_64.tar.gz

    This extracts a piper/ directory containing the piper binary and its shared libraries. Verify it before continuing:

    shell
    cd piper
    ./piper --help

    If you'd rather install as a Python package for easier scripting/integration:

    shell
    python3 -m venv venv
    source venv/bin/activate
    pip install --upgrade pip
    pip install piper-tts fastapi "uvicorn[standard]"

    Both approaches can coexist; the wrapper API below assumes the piper-tts Python package.

    4. Download a voice model

    Piper voices are distributed as an .onnx model plus a .onnx.json config file, per language/speaker, at varying quality tiers (x_low, low, medium, high).

    shell
    mkdir -p /opt/piper/voices/en_US-lessac-medium
    cd /opt/piper/voices/en_US-lessac-medium
    
    wget https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/lessac/medium/en_US-lessac-medium.onnx
    wget https://huggingface.co/rhasspy/piper-voices/resolve/main/en/en_US/lessac/medium/en_US-lessac-medium.onnx.json

    Browse the full voice catalog at the piper-voices Hugging Face repo and repeat for any additional languages/speakers you want to serve.

    Test synthesis directly from the CLI:

    shell
    echo "Hello from RamNode." | ./piper/piper \
      --model /opt/piper/voices/en_US-lessac-medium/en_US-lessac-medium.onnx \
      --output_file /opt/piper/test.wav

    5. Wrap it in a small HTTP API

    Create /opt/piper/server.py:

    shell
    from fastapi import FastAPI
    from fastapi.responses import Response
    from pydantic import BaseModel
    from piper import PiperVoice
    import io, wave, os
    
    VOICE_PATH = os.environ.get(
        "PIPER_VOICE",
        "/opt/piper/voices/en_US-lessac-medium/en_US-lessac-medium.onnx",
    )
    
    app = FastAPI()
    voice = PiperVoice.load(VOICE_PATH)
    
    class SpeakRequest(BaseModel):
        text: str
    
    @app.post("/speak")
    async def speak(req: SpeakRequest):
        buf = io.BytesIO()
        with wave.open(buf, "wb") as wav_file:
            voice.synthesize(req.text, wav_file)
        return Response(content=buf.getvalue(), media_type="audio/wav")
    
    @app.get("/health")
    async def health():
        return {"status": "ok", "voice": VOICE_PATH}

    Test it:

    shell
    uvicorn server:app --host 127.0.0.1 --port 5000

    6. Run it as a systemd service

    As root, create /etc/systemd/system/piper.service:

    shell
    [Unit]
    Description=Piper text-to-speech API
    After=network.target
    
    [Service]
    Type=simple
    User=piper
    Group=piper
    WorkingDirectory=/opt/piper
    Environment=PIPER_VOICE=/opt/piper/voices/en_US-lessac-medium/en_US-lessac-medium.onnx
    ExecStart=/opt/piper/venv/bin/uvicorn server:app --host 127.0.0.1 --port 5000
    Restart=on-failure
    RestartSec=5
    
    [Install]
    WantedBy=multi-user.target
    shell
    systemctl daemon-reload
    systemctl enable --now piper
    systemctl status piper
    shell
    server {
        listen 80;
        server_name tts.example.com;
    
        location / {
            proxy_pass http://127.0.0.1:5000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
    shell
    apt -y install certbot python3-certbot-nginx
    certbot --nginx -d tts.example.com
    ufw delete allow 5000/tcp
    ufw allow "Nginx Full"

    8. Operational notes

    • Multiple voices: load several PiperVoice instances keyed by name/language in the wrapper if you need more than one voice served from the same API; keep each model in its own subdirectory under /opt/piper/voices/.
    • Quality vs. speed: medium quality voices are a good default; high sounds noticeably better but is slower per character — test on your actual vCPU count before committing.
    • Streaming: for long text, synthesize sentence-by-sentence and stream chunks back rather than buffering the whole response, if your client supports chunked audio.
    • Monitoring: add an HTTP check against /health.
    • Updates: pip install -U piper-tts (Python route) or re-download the latest release tarball (binary route), then systemctl restart piper.