⚠️ The latest release is unstable — use preview builds instead
Skip to content

CI/CD Pipelines

We use GitHub Actions for automated building, testing, and deployment.

Overview

PipelineTriggerPurpose
Build ReleaseMerge release candidate PR to masterCreates releases and publishes stable Docker images
Build PreviewPush to masterBuilds and publishes preview Docker images
Validate PRPull requests to masterValidates commits and builds
Deploy ServerAfter preview build / manualDeploys server instances to VPS
Deploy DocsAfter build / manualDeploys documentation to GitHub Pages
Cleanup Preview TagsWeekly schedule / manualDeletes old preview tags from DockerHub
Cleanup CachesWeekly schedule / manualRemoves stale GitHub Actions caches

Build Release Pipeline

Open in Github

The release pipeline handles version bumping, changelog generation, and publishing stable Docker images to DockerHub once a release-please release candidate PR has been merged to master.

Versioning

Version bumps are determined by commit message prefixes:

PrefixVersion BumpExample
fix:Patch (1.0.0 → 1.0.1)Bug fix
feat:Minor (1.0.0 → 1.1.0)New feature added
feat!: or BREAKING CHANGE:Major (1.0.0 → 2.0.0)Breaking change

Docker Images

On release, images are tagged with:

  • sdvd/server:latest - Latest stable version
  • sdvd/server:X.Y.Z - Specific version (e.g., 1.5.0)
sh
# Pull latest stable release
docker pull sdvd/server:latest

# Pull specific version
docker pull sdvd/server:1.5.0

Build Preview Pipeline

Open in Github

WARNING

Preview builds may contain experimental features or bugs. Use stable releases for production servers.

The preview build pipeline runs on every push to master (except docs-only or test-only changes) and creates pre-release Docker images for testing new features before they're officially released.

Preview Versioning

Preview versions follow the format: X.Y.Z-preview.N

  • X.Y.Z - The next expected release version
  • N - Preview counter (increments with each build)

Example: 1.5.0-preview.3 is the third preview build for the upcoming 1.5.0 release.

Preview Docker Images

Preview images are tagged with:

  • sdvd/server:preview - Latest preview build
  • sdvd/server:X.Y.Z-preview.N - Specific preview version (e.g., 1.5.0-preview.3)
sh
# Pull latest preview
docker pull sdvd/server:preview

# Use preview in docker-compose.yml
services:
  server:
    image: sdvd/server:preview

Batching Features

You can merge multiple features before releasing:

Day 1: Merge feat A → 1.1.0-preview.1 published
       Release PR created (1.0.2 → 1.1.0)

Day 2: Merge feat B → 1.2.0-preview.2 published
       Release PR updated (1.0.2 → 1.2.0)

Day 3: Test preview.2 thoroughly

Day 4: Merge Release PR → v1.2.0 released

The Release PR automatically updates as you merge more commits.

Cleanup Preview Tags

Open in Github

Over time, versioned preview tags (X.Y.Z-preview.N) accumulate on DockerHub. This pipeline removes old ones, keeping the 10 most recent per repository (server, steam-service, discord-bot).

The floating preview, latest, and release X.Y.Z tags are never touched.

When It Runs

  • Weekly on Monday at 06:00 UTC
  • Manually via GitHub Actions "Run workflow" button

Manual Options

InputDefaultDescription
keep_count10Number of most recent preview tags to keep
dry_runfalseList tags that would be deleted without deleting

Validate PR Pipeline

Open in Github

The validation pipeline runs on every pull request targeting master. It ensures code quality before merging.

What It Validates

  • Commit messages - Must follow Conventional Commits format
  • Docker build - Ensures the image builds successfully (without pushing)

Deploy Docs Pipeline

Open in Github

Deploys the documentation site to GitHub Pages. Runs automatically after builds or can be triggered manually to rebuild from existing Docker images.

Deploy Server Pipeline

Open in Github

The deploy server pipeline deploys server instances to a VPS. It supports multiple environments that can be individually configured.

When It Runs

  • Automatically after a successful preview build
  • Automatically when a release is published
  • Manually via GitHub Actions "Run workflow" button

Adding a New Server

  1. Create a GitHub Environment matching your server name
  2. Add the environment to the workflow matrix in .github/workflows/deploy-server.yml
  3. Update the workflow dispatch options to include the new environment

Example matrix entry:

yaml
matrix:
    include:
        - environment: public-test
          image_tag: preview
          on_preview: true
          on_release: false

        - environment: production
          image_tag: latest
          on_preview: false
          on_release: true

Setup Requirements

Each deployment target needs a GitHub Environment with its configuration.

Creating Environments

  1. Go to SettingsEnvironments in your repository
  2. Click New environment
  3. Name it to match the workflow matrix (e.g., public-test, production)
  4. Add the secrets listed below

Environment Secrets

All secrets use the DEPLOY_ prefix.

SecretRequiredDescription
DEPLOY_API_KEYNoAPI key for authenticating API/WebSocket requests
DEPLOY_DISCORD_BOT_TOKENNoDiscord bot token for status display
DEPLOY_DISCORD_CHAT_CHANNEL_IDNoDiscord channel ID for chat relay
DEPLOY_GAME_PORTYesUDP port for game connections
DEPLOY_SSH_HOSTYesServer IP address or hostname
DEPLOY_SSH_KEYYesSSH private key (Ed25519 recommended)
DEPLOY_SSH_PORTNoSSH port (defaults to 22)
DEPLOY_SSH_USERYesSSH username
DEPLOY_STEAM_AUTH_PORTYesTCP port for Steam auth service
DEPLOY_STEAM_PASSWORDNo¹Steam account password
DEPLOY_STEAM_REFRESH_TOKENNo¹Steam OAuth refresh token
DEPLOY_STEAM_USERNAMEYesSteam account username
DEPLOY_VNC_PASSWORDYesVNC access password
DEPLOY_VNC_PORTYesTCP port for VNC web interface

¹ Steam authentication: Provide DEPLOY_STEAM_PASSWORD OR DEPLOY_STEAM_REFRESH_TOKEN (or both—if both are set, refresh token is used).

API Key

Generate a secure API key with: bun -e "console.log(require('crypto').randomBytes(32).toString('base64url'))"

TIP

If multiple servers share the same VPS and credentials, repository-level secrets can be used as fallbacks. Environment-level secrets override repository-level secrets with the same name.

VPS Preparation

Before the pipeline can deploy, prepare your VPS.

1. Install Docker

sh
curl -fsSL https://get.docker.com | sh
apt-get install docker-compose-plugin

2. Create Deploy User

Run the setup script from the repository (as root):

sh
curl -fsSL https://raw.githubusercontent.com/stardew-valley-dedicated-server/server/master/tools/create-ssh-user.sh | bash

This creates a github_deploy user with:

  • Docker group membership
  • SSH key for authentication
  • Deploy directory at ~/srv/ (environments deploy to ~/srv/<environment-name>)

The script outputs the private key to add as DEPLOY_SSH_KEY in GitHub.

3. Configure Firewall

sh
# Example for public-test environment
ufw allow 24642/udp  # Game port
ufw allow 5800/tcp   # VNC web interface

Manual Deployment

To manually trigger a deployment:

  1. Go to ActionsDeploy Server
  2. Click Run workflow
  3. Select which environment to deploy (e.g., public-test)
  4. Optionally check "Skip graceful shutdown" for emergency deploys
  5. Click Run workflow

What Gets Deployed

The pipeline:

  1. Creates/updates .env file with secrets and correct IMAGE_VERSION
  2. Copies docker-compose.yml to VPS
  3. Pulls the appropriate Docker images
  4. Restarts containers
  5. Verifies deployment health

TIP

The pipeline uses the same docker-compose.yml from the repository, ensuring consistency between local development and deployed environments. The IMAGE_VERSION environment variable controls which image tag is used.

Cleanup Caches

Open in Github

GitHub Actions caches can accumulate over time. This pipeline removes caches that haven't been accessed in 14 days.

When It Runs

  • Weekly on Sunday at 06:00 UTC
  • Manually via GitHub Actions "Run workflow" button

Discord Notifications

Most pipelines try to send notifications to Discord when builds complete or deployments finish.

To enable notifications, the DISCORD_WEBHOOK_URL repository secret needs to be set with a Discord webhook URL.

Released under the MIT License.