Add Linux development station role with systemd services for code-server and OpenCode

- Implemented handlers for restarting code-server and OpenCode services.
- Created main tasks for setting up a Linux development station, including user management, package installation, and configuration.
- Added validation for OpenCode server credentials and code-server password.
- Introduced tool package management for npm, uv, and go installers.
- Developed templates for systemd service units for code-server and OpenCode.
- Included scripts for starting code-server and OpenCode with SSH agent support.
- Updated inventory and Terraform configuration to include new development host.
This commit is contained in:
2026-07-10 15:44:18 +02:00
parent e1ebe7d139
commit 59b2c10c43
24 changed files with 1523 additions and 28 deletions
@@ -0,0 +1,167 @@
hostname: dev-02.lab.alexpires.me
ansible_user: "{{ lookup('env', 'ANSIBLE_USER') }}"
ansible_host: "10.19.4.210"
linux_dev_station_service_user_enabled: true
linux_dev_station_service_user: "devstation"
linux_dev_station_service_home: "/home/devstation"
linux_dev_station_workspace_dir: "/home/devstation/projects"
linux_dev_station_scripts_dir: "/home/devstation/scripts"
linux_dev_station_logs_dir: "/home/devstation/.local/state/logs"
linux_dev_station_code_server_host: "0.0.0.0"
linux_dev_station_code_server_port: 3000
linux_dev_station_code_server_auth: "password"
linux_dev_station_opencode_server_host: "0.0.0.0"
linux_dev_station_opencode_server_port: 4096
linux_dev_station_manage_systemd: true
linux_dev_station_oh_my_zsh_enabled: true
linux_dev_station_code_server_extensions_enabled: true
linux_dev_station_code_server_extensions:
- ms-python.python
- ms-python.vscode-pylance
- ms-python.debugpy
- golang.go
- ms-azuretools.vscode-docker
- hashicorp.terraform
- opentofu.vscode-opentofu
- redhat.ansible
- redhat.vscode-yaml
- eamodio.gitlens
linux_dev_station_ssh_agent_enabled: true
linux_dev_station_mcp_bootstrap_commands: []
linux_dev_station_mcp_installers_enabled: true
linux_dev_station_mcp_installers:
- name: "chrome-devtools-mcp"
kind: "npm"
package: "chrome-devtools-mcp"
version: "latest"
- name: "playwright-mcp"
kind: "npm"
package: "@playwright/mcp"
version: "latest"
- name: "aws-documentation-mcp"
kind: "uv"
package: "awslabs.aws-documentation-mcp-server"
version: "latest"
binary: "awslabs.aws-documentation-mcp-server"
- name: "gitea-mcp"
kind: "go"
module: "gitea.com/gitea/gitea-mcp"
version: "latest"
binary: "gitea-mcp"
linux_dev_station_opencode_extra_environment:
GITEA_MCP_HOST: "https://code.alexpires.me"
GITEA_APP_TOKEN: "{{ lookup('env', 'GITEA_APP_TOKEN', default='') }}"
linux_dev_station_opencode_config:
"$schema": "https://opencode.ai/config.json"
provider:
alexpires-me:
name: "alexpires.me"
npm: "@ai-sdk/openai-compatible"
options:
baseURL: "https://open-webui.lab.alexpires.me/api"
apiKey: "{{ lookup('env', 'OPEN_WEBUI_API_KEY', default='') }}"
timeout: 3000000
chunkTimeout: 3000000
models:
gemma-4-12b-q8-0:
name: "gemma-4-12b-q8-0"
gemma-4-26b-a4b-ud-iq4-xs:
name: "gemma-4-26b-a4b-ud-iq4-xs"
gemma-4-31b-iq4-xs:
name: "gemma-4-31b-iq4-xs"
gpt-oss-20b-q4-0:
name: "gpt-oss-20b-q4-0"
gpt-oss-20b-q8-0:
name: "gpt-oss-20b-q8-0"
qwen3-5-35b-a3b-q4-k-m:
name: "qwen3-5-35b-a3b-q4-k-m"
qwen3-5-9b-q8-0:
name: "qwen3-5-9b-q8-0"
qwen3-6-27b-mtp-q4-0:
name: "qwen3-6-27b-mtp-q4-0"
qwen3-6-27b-q4-k-m:
name: "qwen3-6-27b-q4-k-m"
qwen3-6-35b-a3b-mtp-ud-q4-m:
name: "qwen3-6-35b-a3b-mtp-ud-q4-m"
qwen3-6-35b-a3b-ud-q4-k-m:
name: "qwen3-6-35b-a3b-ud-q4-k-m"
mcp:
deepwiki:
type: "remote"
url: "https://mcp.deepwiki.com/mcp"
enabled: true
terraform:
type: "remote"
url: "http://terraform-mcp.lab.alexpires.me/mcp"
enabled: true
opentofu:
type: "remote"
url: "https://mcp.opentofu.org/mcp"
enabled: true
microsoft_learn:
type: "remote"
url: "https://learn.microsoft.com/api/mcp"
enabled: true
mozilla_developer:
type: "remote"
url: "https://mcp.mdn.mozilla.net/"
enabled: true
chrome_devtools:
type: "local"
command:
- "npx"
- "-y"
- "chrome-devtools-mcp@latest"
- "--headless"
- "--isolated"
enabled: true
playwright:
type: "local"
command:
- "npx"
- "-y"
- "@playwright/mcp@latest"
- "--headless"
- "--isolated"
enabled: true
aws_documentation:
type: "local"
command:
- "uvx"
- "awslabs.aws-documentation-mcp-server@latest"
enabled: true
gitea:
type: "local"
command:
- "gitea-mcp"
- "-t"
- "stdio"
- "-H"
- "{env:GITEA_MCP_HOST}"
- "-T"
- "{env:GITEA_APP_TOKEN}"
enabled: true
compaction:
auto: true
prune: true
reserved: 32768
linux_dev_station_service_user_ssh_enabled: true
linux_dev_station_service_user_ssh_authorized_keys: []
# Firewall ports
ufw_enable: true
fw_allowed_ports:
- { rule: "allow", port: "22", proto: "tcp", from: "10.5.5.5/32" } # SSH Access (VPN device)
- { rule: "allow", port: "22", proto: "tcp", from: "10.19.4.0/24" } # SSH Access (local network)
- { rule: "allow", port: "3000", proto: "tcp", from: "10.19.4.0/24" } # code-server (local network only)
- { rule: "allow", port: "4096", proto: "tcp", from: "10.19.4.0/24" } # opencode (local network only)
- { rule: "allow", port: "9090", proto: "tcp", from: "10.19.4.0/24" } # opencode (local network only)
+41 -26
View File
@@ -118,25 +118,25 @@ llama_server_devices:
- "/dev/dri:/dev/dri:rw"
llama_server_models:
# TODO: replace revision values with pinned commit SHAs for strict reproducibility.
# # TODO: replace revision values with pinned commit SHAs for strict reproducibility.
- name: "gemma-4-12b-q8-0"
model_id: "unsloth/gemma-4-12b-it-GGUF"
quant: "Q8_0"
revision: "main"
preset:
ctx-size: 98304
# preset:
# ctx-size: 98304
- name: "gemma-4-26b-a4b-ud-iq4-xs"
model_id: "unsloth/gemma-4-26B-A4B-it-GGUF"
quant: "UD-IQ4_XS"
revision: "main"
preset:
ctx-size: 98304
# preset:
# ctx-size: 98304
- name: "gemma-4-31b-iq4-xs"
model_id: "unsloth/gemma-4-31B-it-GGUF"
quant: "IQ4_XS"
revision: "main"
preset:
ctx-size: 98304
# preset:
# ctx-size: 98304
- name: "gpt-oss-20b-q4-0"
model_id: "unsloth/gpt-oss-20b-GGUF"
quant: "Q4_0"
@@ -149,44 +149,56 @@ llama_server_models:
model_id: "unsloth/Qwen3.5-27B-GGUF"
quant: "Q4_K_M"
revision: "main"
preset:
ctx-size: 98304
# preset:
# ctx-size: 98304
- name: "qwen3-5-35b-a3b-q4-k-m"
model_id: "unsloth/Qwen3.5-35B-A3B-GGUF"
quant: "Q4_K_M"
revision: "main"
preset:
ctx-size: 98304
# preset:
# ctx-size: 98304
- name: "qwen3-5-9b-q8-0"
model_id: "unsloth/Qwen3.5-9B-GGUF"
quant: "Q8_0"
revision: "main"
preset:
ctx-size: 98304
# preset:
# ctx-size: 98304
- name: "qwen3-6-27b-q4-k-m"
model_id: "unsloth/Qwen3.6-27B-GGUF"
quant: "Q4_K_M"
revision: "main"
preset:
ctx-size: 98304
# preset:
# ctx-size: 98304
- name: "qwen3-6-35b-a3b-ud-q4-k-m"
model_id: "unsloth/Qwen3.6-35B-A3B-GGUF"
quant: "UD-Q4_K_M"
revision: "main"
# preset:
# ctx-size: 98304
- name: "qwen3-6-27b-mtp-q4-0"
model_id: "unsloth/Qwen3.6-27B-MTP-GGUF"
quant: "Q4_0"
revision: "main"
preset:
ctx-size: 98304
temperature: 0.6
top_p: 0.95
top_k: 20
min_p: 0.0
presence_penalty: 0.0
repeat_penalty: 1.0
# ctx-size: 98304
- name: "qwen3-6-35b-a3b-mtp-ud-q4-m"
model_id: "unsloth/Qwen3.6-35B-A3B-MTP-GGUF"
quant: "UD-Q4_K_M"
revision: "main"
preset:
ctx-size: 98304
- name: "qwen3-6-35b-a3b-ud-q4-k-m"
model_id: "unsloth/Qwen3.6-35B-A3B-GGUF"
quant: "UD-Q4_K_M"
revision: "main"
preset:
ctx-size: 98304
temperature: 0.6
top_p: 0.95
top_k: 20
min_p: 0.0
presence_penalty: 0.0
repeat_penalty: 1.0
# ctx-size: 98304
llama_server_argv_extra:
[
@@ -195,15 +207,15 @@ llama_server_argv_extra:
"-np",
-1,
"-b",
1024,
2048,
"-ub",
512,
"-fa",
"on",
"-ctk",
"q4_0",
"-ctv",
"q8_0",
"-ctv",
"q4_0",
"-cram",
-1,
"-sm",
@@ -216,11 +228,14 @@ llama_server_argv_extra:
"off",
"-mg",
1,
"--timeout",
1800,
"--mmap",
"--spec-type",
"draft-mtp",
"--spec-draft-n-max",
2,
"--swa-full",
]
auto_suspend_enabled: true
@@ -76,6 +76,8 @@ macos_dev_station_opencode_config:
options:
baseURL: "https://open-webui.lab.alexpires.me/api"
apiKey: "{{ lookup('env', 'OPEN_WEBUI_API_KEY', default='') }}"
timeout: 3000000
chunkTimeout: 3000000
models:
gemma-4-12b-q8-0:
name: "gemma-4-12b-q8-0"