Compare commits
21 Commits
b686486c93
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 696813fc72 | |||
| d099c4dcc2 | |||
| 177d7b480c | |||
| 641beb1f81 | |||
| 0e5712a05e | |||
| 318c8e5ae1 | |||
| 75fcfb90bf | |||
| 41c1543979 | |||
| 8663b87f0f | |||
| d4e2b4a241 | |||
| 695acaa48a | |||
| 71c46ad953 | |||
| fdacbec5ea | |||
| 16bb096e34 | |||
| 49a25fff41 | |||
| 829bb876e0 | |||
| 01337c1e84 | |||
| fca39e54b3 | |||
| 9c64bb856f | |||
| 927d57e9c9 | |||
| 149fd307f2 |
@@ -50,6 +50,30 @@ client {
|
||||
path = "/ssd/sqlite/"
|
||||
read_only = false
|
||||
}
|
||||
|
||||
host_volume "jellyfinCache" {
|
||||
path = "/hdd/multimedia/cache/"
|
||||
read_only = false
|
||||
}
|
||||
|
||||
host_volume "jellyfinConfig" {
|
||||
path = "/hdd/multimedia/config/"
|
||||
read_only = false
|
||||
}
|
||||
|
||||
host_volume "media" {
|
||||
path = "/hdd/multimedia/media/"
|
||||
read_only = false
|
||||
}
|
||||
}
|
||||
|
||||
plugin "docker" {
|
||||
config {
|
||||
allow_privileged = true
|
||||
volumes {
|
||||
enabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui {
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
- name: Enable community packages
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/apk/repositories
|
||||
regexp: '^#http://dl-cdn.alpinelinux.org/alpine/v3.18/community'
|
||||
line: 'http://dl-cdn.alpinelinux.org/alpine/v3.18/community'
|
||||
regexp: "^#http://dl-cdn.alpinelinux.org/alpine/v3.18/community"
|
||||
line: "http://dl-cdn.alpinelinux.org/alpine/v3.18/community"
|
||||
state: present
|
||||
|
||||
- name: Update apk packages
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
- name: Enable community packages
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/apk/repositories
|
||||
regexp: '^#http://dl-cdn.alpinelinux.org/alpine/v3.18/community'
|
||||
line: 'http://dl-cdn.alpinelinux.org/alpine/v3.18/community'
|
||||
regexp: "^#http://dl-cdn.alpinelinux.org/alpine/v3.18/community"
|
||||
line: "http://dl-cdn.alpinelinux.org/alpine/v3.18/community"
|
||||
state: present
|
||||
|
||||
- name: Update apk packages
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
- name: Enable community packages
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/apk/repositories
|
||||
regexp: '^#http://dl-cdn.alpinelinux.org/alpine/v3.18/community'
|
||||
line: 'http://dl-cdn.alpinelinux.org/alpine/v3.18/community'
|
||||
regexp: "^#http://dl-cdn.alpinelinux.org/alpine/v3.18/community"
|
||||
line: "http://dl-cdn.alpinelinux.org/alpine/v3.18/community"
|
||||
state: present
|
||||
|
||||
- name: Update apk packages
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
- name: Install Caddy on Alpine Linux
|
||||
- name: Install Traefik on Alpine Linux
|
||||
hosts: all
|
||||
|
||||
tasks:
|
||||
- name: Enable community packages
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/apk/repositories
|
||||
regexp: '^#http://dl-cdn.alpinelinux.org/alpine/v3.18/community'
|
||||
line: 'http://dl-cdn.alpinelinux.org/alpine/v3.18/community'
|
||||
regexp: "^#http://dl-cdn.alpinelinux.org/alpine/v3.18/community"
|
||||
line: "http://dl-cdn.alpinelinux.org/alpine/v3.18/community"
|
||||
state: present
|
||||
|
||||
- name: Update apk packages
|
||||
|
||||
306
nomad_jobs/apps/penpot-standalone.nomad.hcl
Normal file
306
nomad_jobs/apps/penpot-standalone.nomad.hcl
Normal file
@@ -0,0 +1,306 @@
|
||||
locals {
|
||||
TRAEFIK_DOMAIN = "penpot.example.local"
|
||||
PENPOT_PUBLIC_URI = "https://${local.TRAEFIK_DOMAIN}"
|
||||
PENPOT_SECRET_KEY = "op://InfraSecrets/7hbsxng22unjqc4wkj62qniu2u/credential" # Try running `openssl rand -hex 32` to generate a random secret key
|
||||
PENPOT_FLAGS = "enable-demo-users"
|
||||
}
|
||||
|
||||
job "penpot-standalone" {
|
||||
datacenters = ["dc1"]
|
||||
|
||||
group "frontend" {
|
||||
count = 1
|
||||
|
||||
network {
|
||||
mode = "bridge"
|
||||
port "ingress" {
|
||||
to = 80
|
||||
}
|
||||
}
|
||||
|
||||
# Expose frontend to internet through traefik
|
||||
service {
|
||||
name = "penpot-standalone"
|
||||
port = "ingress"
|
||||
|
||||
tags = [
|
||||
"traefik.enable=true",
|
||||
"traefik.http.routers.penpot-standalone.tls=true",
|
||||
"traefik.http.routers.penpot-standalone.entrypoints=websecure",
|
||||
"traefik.http.routers.penpot-standalone.rule=Host(`${local.TRAEFIK_DOMAIN}`)",
|
||||
]
|
||||
|
||||
connect {
|
||||
sidecar_service {
|
||||
proxy {
|
||||
upstreams {
|
||||
destination_name = "penpot-standalone-backend"
|
||||
local_bind_address = "127.0.0.1"
|
||||
local_bind_port = 6060
|
||||
}
|
||||
upstreams {
|
||||
destination_name = "penpot-standalone-exporter"
|
||||
local_bind_address = "127.0.0.1"
|
||||
local_bind_port = 6061
|
||||
}
|
||||
upstreams {
|
||||
destination_name = "minio"
|
||||
local_bind_address = "127.0.0.1"
|
||||
local_bind_port = 9000
|
||||
}
|
||||
}
|
||||
tags = ["traefik.enable=false"] # Hide service from traefik
|
||||
}
|
||||
}
|
||||
|
||||
check {
|
||||
type = "http"
|
||||
path = "/"
|
||||
interval = "10s"
|
||||
timeout = "2s"
|
||||
}
|
||||
}
|
||||
|
||||
task "frontend" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "penpotapp/frontend:2.0.1"
|
||||
ports = ["ingress"]
|
||||
}
|
||||
|
||||
env {
|
||||
PENPOT_PUBLIC_URI = local.PENPOT_PUBLIC_URI
|
||||
PENPOT_BACKEND_URI = "http://127.0.0.1:6060"
|
||||
PENPOT_EXPORTER_URI = "http://127.0.0.1:6061"
|
||||
|
||||
PENPOT_FLAGS = local.PENPOT_FLAGS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// penpot-standalone-backend
|
||||
group "backend" {
|
||||
|
||||
network {
|
||||
mode = "bridge"
|
||||
}
|
||||
|
||||
service {
|
||||
# Make available to other services by the 'penpot-backend' name
|
||||
name = "penpot-standalone-backend"
|
||||
port = "6060"
|
||||
tags = ["traefik.enable=false"] # Hide redis from traefik
|
||||
|
||||
# Make available through the consul service mesh
|
||||
connect {
|
||||
sidecar_service {
|
||||
proxy {
|
||||
upstreams {
|
||||
destination_name = "penpot-standalone-postgres"
|
||||
local_bind_address = "127.0.0.1"
|
||||
local_bind_port = 5432
|
||||
}
|
||||
upstreams {
|
||||
destination_name = "penpot-standalone-redis-cache"
|
||||
local_bind_address = "127.0.0.1"
|
||||
local_bind_port = 6379
|
||||
}
|
||||
upstreams {
|
||||
destination_name = "penpot-standalone-fake-smtp"
|
||||
local_bind_address = "127.0.0.1"
|
||||
local_bind_port = 1025
|
||||
}
|
||||
}
|
||||
tags = ["traefik.enable=false"] # Hide penpot-backend envoy from traefik
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task "backend" {
|
||||
driver = "docker"
|
||||
config {
|
||||
image = "penpotapp/backend:2.0.1"
|
||||
ports = ["ingress"]
|
||||
}
|
||||
|
||||
env {
|
||||
PENPOT_PUBLIC_URI = local.PENPOT_PUBLIC_URI
|
||||
PENPOT_SECRET_KEY = local.PENPOT_SECRET_KEY
|
||||
PENPOT_FLAGS = local.PENPOT_FLAGS
|
||||
PENPOT_DATABASE_URI = "postgresql://127.0.0.1:5432/penpot"
|
||||
PENPOT_DATABASE_USERNAME = "penpot"
|
||||
PENPOT_DATABASE_PASSWORD = "not-a-secure-password"
|
||||
PENPOT_REDIS_URI = "redis://127.0.0.1:6379/0"
|
||||
PENPOT_TELEMERY_ENABLED = "false"
|
||||
|
||||
PENPOT_ASSETS_STORAGE_BACKEND = "assets-fs"
|
||||
PENPOT_STORAGE_ASSETS_FS_DIRECTORY = "/opt/data/assets"
|
||||
|
||||
PENPOT_SMTP_DEFAULT_FROM = "no-reply+penpot-standalone@example.local"
|
||||
PENPOT_SMTP_DEFAULT_REPLY_TO = "no-reply+penpot-standalone@example.local"
|
||||
PENPOT_SMTP_HOST = "127.0.0.1"
|
||||
PENPOT_SMTP_PORT = "1025"
|
||||
PENPOT_SMTP_USERNAME = ""
|
||||
PENPOT_SMTP_PASSWORD = ""
|
||||
PENPOT_SMTP_TLS = "false"
|
||||
PENPOT_SMTP_SSL = "false"
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 8000
|
||||
memory = 1024
|
||||
memory_max = 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// penpot-standalone-exporter
|
||||
group "exporter" {
|
||||
|
||||
network {
|
||||
mode = "bridge"
|
||||
}
|
||||
|
||||
task "exporter" {
|
||||
driver = "docker"
|
||||
config {
|
||||
image = "penpotapp/exporter:2.0.1"
|
||||
}
|
||||
|
||||
env {
|
||||
PENPOT_PUBLIC_URI = local.PENPOT_PUBLIC_URI
|
||||
PENPOT_REDIS_URI = "redis://127.0.0.1:6379/0"
|
||||
}
|
||||
}
|
||||
|
||||
service {
|
||||
name = "penpot-standalone-exporter"
|
||||
port = "6061"
|
||||
tags = ["traefik.enable=false"] # Hide envoy from traefik
|
||||
|
||||
connect {
|
||||
sidecar_service {
|
||||
proxy {
|
||||
upstreams {
|
||||
destination_name = "penpot-standalone-redis-cache"
|
||||
local_bind_address = "127.0.0.1"
|
||||
local_bind_port = 6379
|
||||
}
|
||||
}
|
||||
tags = ["traefik.enable=false"] # Hide envoy from traefik
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// penpot-standalone-postgres
|
||||
group "postgres" {
|
||||
count = 1
|
||||
|
||||
network {
|
||||
mode = "bridge"
|
||||
port "ingress" {
|
||||
to = 5432
|
||||
}
|
||||
}
|
||||
|
||||
service {
|
||||
# Make available to other services by the 'postgres' name
|
||||
name = "penpot-standalone-postgres"
|
||||
port = "5432"
|
||||
tags = ["traefik.enable=false"] # Hide postgres from traefik
|
||||
|
||||
# Make available through the consul service mesh
|
||||
connect {
|
||||
sidecar_service {
|
||||
tags = ["traefik.enable=false"] # Hide postgres envoy from traefik
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task "postgres" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "postgres:16.1-alpine3.19"
|
||||
ports = ["ingress"]
|
||||
}
|
||||
|
||||
env = {
|
||||
POSTGRES_USER="penpot",
|
||||
POSTGRES_PASSWORD="not-a-secure-password"
|
||||
POSTGRES_DB="penpot"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// penpot-standalone-redis-cache
|
||||
group "redis" {
|
||||
count = 1
|
||||
|
||||
network {
|
||||
mode = "bridge"
|
||||
port "redis" {
|
||||
to = 6379
|
||||
}
|
||||
}
|
||||
|
||||
service {
|
||||
# Make available to other services by the 'redis-cache' name
|
||||
name = "penpot-standalone-redis-cache"
|
||||
port = "6379"
|
||||
tags = ["traefik.enable=false"] # Hide redis from traefik
|
||||
|
||||
# Make available through the consul service mesh
|
||||
connect {
|
||||
sidecar_service {
|
||||
tags = ["traefik.enable=false"] # Hide redis envoy from traefik
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task "redis" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "redis:7.2.3-alpine"
|
||||
ports = ["redis"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// penpot-standalone-fake-smtp
|
||||
group "mailcatcher" {
|
||||
count = 1
|
||||
|
||||
network {
|
||||
mode = "bridge"
|
||||
port "webUI" {
|
||||
to = 1080
|
||||
}
|
||||
}
|
||||
|
||||
service {
|
||||
# Make available to other services by the 'fake-smtp' name
|
||||
name = "penpot-standalone-fake-smtp"
|
||||
port = "1025"
|
||||
tags = ["traefik.enable=false"] # Hide redis from traefik
|
||||
|
||||
connect {
|
||||
sidecar_service {
|
||||
tags = ["traefik.enable=false"] # Hide redis envoy from traefik
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task "mailcatcher" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "sj26/mailcatcher:latest"
|
||||
ports = ["webUI"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
nomad_jobs/services/gitea/dockerfile_gitea-act-runner
Normal file
3
nomad_jobs/services/gitea/dockerfile_gitea-act-runner
Normal file
@@ -0,0 +1,3 @@
|
||||
FROM gitea/act_runner:0.2.10-dind-rootless
|
||||
USER root
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
locals {
|
||||
SUBDOMAIN = "git." // End with dot or leave blamk for root domain
|
||||
DOMAIN = "example.local"
|
||||
TRAEFIK_DOMAIN = "${local.SUBDOMAIN}${local.DOMAIN}"
|
||||
}
|
||||
|
||||
job "gitea" {
|
||||
datacenters = ["dc1"]
|
||||
type = "service"
|
||||
@@ -8,7 +14,7 @@ job "gitea" {
|
||||
network {
|
||||
mode = "bridge"
|
||||
port "ingress" {
|
||||
to = 3000
|
||||
static = 3000
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +44,7 @@ job "gitea" {
|
||||
"traefik.enable=true",
|
||||
"traefik.http.routers.gitea.tls=true",
|
||||
"traefik.http.routers.gitea.entrypoints=websecure",
|
||||
"traefik.http.routers.gitea.rule=Host(`git.example.local`)"
|
||||
"traefik.http.routers.gitea.rule=Host(`${local.TRAEFIK_DOMAIN}`)"
|
||||
]
|
||||
|
||||
check {
|
||||
@@ -53,7 +59,7 @@ job "gitea" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "gitea/gitea:1.21.1"
|
||||
image = "gitea/gitea:1.22.1"
|
||||
ports = ["ingress"]
|
||||
}
|
||||
|
||||
@@ -61,6 +67,11 @@ job "gitea" {
|
||||
volume = "gitea-data"
|
||||
destination = "/data"
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 3000
|
||||
memory = 2000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,16 +11,42 @@ You will need to modify the job spec items listed under [TODO](./readme.md#TODO)
|
||||
- [Postgres](../postgres/readme.md)
|
||||
|
||||
## TODO
|
||||
If you want to deploy this, you will need to verify you have a valid host volume and update the domain name in the job spec.
|
||||
If you want to deploy this, you will need to verify you have a valid host volume.
|
||||
|
||||
| Line | Default | Adjustment |
|
||||
| --- | --- | --- |
|
||||
| 17 | `source = "gitea-data"` | Change `gitea-data` to a valid host volume name |
|
||||
| 46 | `"traefik.http.routers.caddy.rule=Host('git.example.com')"` | Change `git.example.com` to your domain name |
|
||||
| 66 | `volume = "gitea-data"` | Change `gitea-data` to the host volume defined on line 15 if applicable |
|
||||
|
||||
> To make the instance accessible through TRAEFIK you will need to define the domain to listen on by setting the value(s) on lines 2 and 3.
|
||||
|
||||
|
||||
## Configuring Gitea
|
||||
There is no need to embed secrets in the nomad job spec. When you first visit the domain name you configured, you will be prompted to configure Gitea. Postgres should be mounted to the container on the standard `5432` port so you can select postgres as the database type and use `127.0.0.1:5432` as the address and input the username, password, and database name you created for Gitea to use.
|
||||
|
||||
If you need help making those credentials, take a look at the [postgres readme](../postgres/readme.md#make-a-new-database).
|
||||
|
||||
# Adding CI/CD
|
||||
Gitea has a fork of act runner that can be used to run Github actions. In order to deploy this with Nomad, you will need to leverage Docker in Docker (DinD) with privileged mode enabled in Docker or pay for the bussiness plan of Docker to have better app isolation. The default runner image provided by Gitea was failing to start DinD Daemon so I included a dockerfile that you can use to specify that the container should be ran as the root user.
|
||||
|
||||
1. Build Image
|
||||
```bash
|
||||
docker build --network host --platform linux/amd64 -t <your_gitea_domain>/caleb/nomad_act_runner:0.0.1 .
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> You may not need to specify the platform flag. If you use Apple Silicon but deploy to X86, you will need to include the flag.
|
||||
|
||||
2. Push Image
|
||||
```bash
|
||||
docker push <your_gitea_domain>/caleb/nomad_act_runner:0.0.1
|
||||
```
|
||||
|
||||
4. Run the nomad job with the Gitea_Runner_Token
|
||||
```bash
|
||||
nomad job run -var "grt=<your_token>" -var "domain=<gitea_domain>" runner.nomad.hcl
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> If you prefer to not use cli variables, you can update the top of the Nomad Job Spec and manually put in the env variables.
|
||||
|
||||
|
||||
71
nomad_jobs/services/gitea/runner.nomad.hcl
Normal file
71
nomad_jobs/services/gitea/runner.nomad.hcl
Normal file
@@ -0,0 +1,71 @@
|
||||
variable "grt" {
|
||||
type = string
|
||||
description = "Gitea runner token"
|
||||
}
|
||||
|
||||
variable "domain" {
|
||||
type = string
|
||||
description = "Gitea Domain Name"
|
||||
}
|
||||
|
||||
locals {
|
||||
GITEA_RUNNER_TOKEN = var.grt # Replace with raw token surrounded by quotes if you don't want to pass via cli or using web ui
|
||||
GITEA_DOMAIN = var.domain # Replace with domain surrounded by quotes if you don't want to pass via cli or using web ui
|
||||
GITEA_RUNNER_NAME = "${NOMAD_TASK_NAME}-${NOMAD_ALLOC_INDEX}"
|
||||
}
|
||||
|
||||
job "gitea-runner" {
|
||||
datacenters = ["dc1"]
|
||||
type = "service"
|
||||
|
||||
group "application" {
|
||||
count = 1
|
||||
|
||||
scaling {
|
||||
enabled = true
|
||||
min = 1
|
||||
max = 5
|
||||
}
|
||||
|
||||
network {
|
||||
mode = "bridge"
|
||||
}
|
||||
|
||||
service {
|
||||
name = "gitea-runner"
|
||||
|
||||
connect {
|
||||
sidecar_service {
|
||||
proxy {
|
||||
upstreams {
|
||||
destination_name = "gitea"
|
||||
local_bind_address = "127.0.0.1"
|
||||
local_bind_port = 3000
|
||||
}
|
||||
}
|
||||
tags = ["traefik.enable=false"] # Hide envoy from traefik
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task "gitea-runner" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "${local.GITEA_DOMAIN}/caleb/nomad_act_runner:0.0.1"
|
||||
privileged = true
|
||||
}
|
||||
|
||||
env = {
|
||||
GITEA_INSTANCE_URL="http://${NOMAD_UPSTREAM_ADDR_gitea}"
|
||||
GITEA_RUNNER_REGISTRATION_TOKEN="${local.GITEA_RUNNER_TOKEN}"
|
||||
GITEA_RUNNER_NAME="${local.GITEA_RUNNER_NAME}"
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 2000
|
||||
memory = 2000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
89
nomad_jobs/services/jellyfin/jellyfin.nomad.hcl
Normal file
89
nomad_jobs/services/jellyfin/jellyfin.nomad.hcl
Normal file
@@ -0,0 +1,89 @@
|
||||
locals {
|
||||
SUBDOMAIN = "jellyfin." // End with dot or leave blamk for root domain
|
||||
DOMAIN = "example.com"
|
||||
TRAEFIK_DOMAIN = "${local.SUBDOMAIN}${local.DOMAIN}"
|
||||
}
|
||||
|
||||
job "jellyfin" {
|
||||
datacenters = ["dc1"]
|
||||
type = "service"
|
||||
|
||||
group "application" {
|
||||
count = 1
|
||||
|
||||
network {
|
||||
mode = "host"
|
||||
port "httpIngress" { static = 8096 }
|
||||
port "serviceDiscovery" { static = 1900 }
|
||||
port "clientDiscovery" { static = 7359 }
|
||||
}
|
||||
|
||||
volume "jellyfin-cache" {
|
||||
type = "host"
|
||||
source = "jellyfinCache"
|
||||
}
|
||||
|
||||
volume "jellyfin-config" {
|
||||
type = "host"
|
||||
source = "jellyfinConfig"
|
||||
}
|
||||
|
||||
volume "jellyfin-data" {
|
||||
type = "host"
|
||||
source = "media"
|
||||
}
|
||||
|
||||
service {
|
||||
name = "jellyfin"
|
||||
port = "httpIngress"
|
||||
|
||||
tags = [
|
||||
"traefik.enable=true",
|
||||
"traefik.http.routers.jellyfin.tls=true",
|
||||
"traefik.http.routers.jellyfin.entrypoints=websecure",
|
||||
"traefik.http.routers.jellyfin.rule=Host(`${local.TRAEFIK_DOMAIN}`)"
|
||||
]
|
||||
|
||||
check {
|
||||
type = "http"
|
||||
path = "/health"
|
||||
interval = "10s"
|
||||
timeout = "2s"
|
||||
}
|
||||
}
|
||||
|
||||
task "jellyfin" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "jellyfin/jellyfin:2024080505"
|
||||
ports = ["httpIngress", "serviceDiscovery", "clientDiscovery"]
|
||||
}
|
||||
|
||||
env = {
|
||||
JELLYFIN_PublishedServerUrl="${local.TRAEFIK_DOMAIN}"
|
||||
}
|
||||
|
||||
volume_mount {
|
||||
volume = "jellyfin-cache"
|
||||
destination = "/cache"
|
||||
}
|
||||
|
||||
volume_mount {
|
||||
volume = "jellyfin-config"
|
||||
destination = "/config"
|
||||
}
|
||||
|
||||
volume_mount {
|
||||
volume = "jellyfin-data"
|
||||
destination = "/media"
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 2000
|
||||
memory = 1024
|
||||
memory_max = 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
36
nomad_jobs/services/jellyfin/readme.md
Normal file
36
nomad_jobs/services/jellyfin/readme.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Jellyfin
|
||||
|
||||
Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. It is an alternative to the proprietary Emby and Plex, to provide media from a dedicated server to end-user devices via multiple apps.
|
||||
|
||||
## Nomad Job for Gitea
|
||||
|
||||
You will need to modify the job spec items listed under [TODO](./readme.md#TODO) but there are no Jellyfin specific adjustments needed. If you run it, it will register with consul and be available to Traefik for routing. If the domain name is configured correctly, you should be able to reach the Jellyfin setup page to make the needed configuration changes such as defining the media libraries.
|
||||
|
||||
## Service Dependencies
|
||||
|
||||
- A Valid [Host Volume](../../../host_init/README.md#storage-and-zfs) For Cache
|
||||
- A Valid [Host Volume](../../../host_init/README.md#storage-and-zfs) For Config
|
||||
- A Valid [Host Volume](../../../host_init/README.md#storage-and-zfs) For Media
|
||||
|
||||
## TODO
|
||||
|
||||
If you want to deploy this, you will need to verify you have the necessary host volumes.
|
||||
|
||||
| Line | Default | Adjustment |
|
||||
| ---- | --------------------------- | --------------------------------------------------------------------------- |
|
||||
| 23 | `source = "jellyfinCache"` | Change `jellyfinCache` to a valid host volume name |
|
||||
| 28 | `source = "jellyfinConfig"` | Change `jellyfinConfig` to a valid host volume name |
|
||||
| 33 | `source = "media"` | Change `media` to a valid host volume name |
|
||||
| 68 | `volume = "jellyfinCache"` | Change `jellyfinCache` to the host volume defined on line 21 if applicable |
|
||||
| 74 | `volume = "jellyfinConfig"` | Change `jellyfinConfig` to the host volume defined on line 26 if applicable |
|
||||
| 79 | `volume = "media"` | Change `media` to the host volume defined on line 31 if applicable |
|
||||
|
||||
> To make the instance accessible through TRAEFIK you will need to define the domain to listen on by setting the value(s) on lines 2 and 3.
|
||||
|
||||
## Configuring Jellyfin
|
||||
|
||||
There is no need to embed secrets in the nomad job spec. When you first visit the domain name you configured, you will be prompted to configure Jellyfin.
|
||||
|
||||
> I recomend using a single root directory for media and then creating subdirectories for each type of media. This will make it easier to manage the media via SFTP and to configure Jellyfin.
|
||||
|
||||
> If this is deployed on Alpine Linux, you won't be able to pass through dedicated NVIDIA hardware because `nvidia-container-toolkit` is not available on MUSL. You will need to use a different root operating system like Ubuntu if you want hardware acceleration with NVIDIA hardware.
|
||||
241
nomad_jobs/services/penpot/penpot.nomad.hcl
Normal file
241
nomad_jobs/services/penpot/penpot.nomad.hcl
Normal file
@@ -0,0 +1,241 @@
|
||||
# Listening Domain
|
||||
locals {
|
||||
SUBDOMAIN = "penpot." // End with dot or leave blamk for root domain
|
||||
DOMAIN = "example.local"
|
||||
TRAEFIK_DOMAIN = "${local.SUBDOMAIN}${local.DOMAIN}"
|
||||
}
|
||||
|
||||
# Application routing environment variables
|
||||
locals {
|
||||
PENPOT_PUBLIC_URI = "https://${local.TRAEFIK_DOMAIN}"
|
||||
PENPOT_BACKEND_URI = "http://127.0.0.1:6060"
|
||||
PENPOT_EXPORTER_URI = "http://127.0.0.1:6061"
|
||||
PENPOT_FLAGS = "enable-smtp enable-registration enable-login-with-password enable-demo-users"
|
||||
PENPOT_SECRET_KEY = "op://InfraSecrets/7hbsxng22unjqc4wkj62qniu2u/credential" # Try running `openssl rand -hex 32` to generate a random secret key
|
||||
PENPOT_DATABASE_URI = "postgresql://127.0.0.1:5432/penpot"
|
||||
PENPOT_DATABASE_USERNAME = "op://InfraSecrets/Postgres - Penpot User/username"
|
||||
PENPOT_DATABASE_PASSWORD = "op://InfraSecrets/Postgres - Penpot User/password"
|
||||
PENPOT_REDIS_URI = "redis://127.0.0.1:6379/0"
|
||||
PENPOT_TELEMERY_ENABLED = "false"
|
||||
}
|
||||
|
||||
# Assets storage environment variables (fs or s3)
|
||||
locals {
|
||||
// PENPOT_ASSETS_STORAGE_BACKEND = "assets-fs"
|
||||
PENPOT_STORAGE_ASSETS_FS_DIRECTORY = "/opt/data/assets"
|
||||
|
||||
PENPOT_ASSETS_STORAGE_BACKEND = "assets-s3"
|
||||
AWS_ACCESS_KEY_ID = "op://InfraSecrets/Penpot S3 Key/username"
|
||||
AWS_SECRET_ACCESS_KEY = "op://InfraSecrets/Penpot S3 Key/credential"
|
||||
PENPOT_STORAGE_ASSETS_S3_ENDPOINT = "http://127.0.0.1:9000"
|
||||
PENPOT_STORAGE_ASSETS_S3_BUCKET = "penpot"
|
||||
}
|
||||
|
||||
# SMTP environment variables
|
||||
locals {
|
||||
PENPOT_SMTP_DEFAULT_FROM = "no-reply+penpot@${local.DOMAIN}"
|
||||
PENPOT_SMTP_DEFAULT_REPLY_TO = "no-reply+penpot@${local.DOMAIN}"
|
||||
PENPOT_SMTP_HOST = "127.0.0.1"
|
||||
PENPOT_SMTP_PORT = "1025"
|
||||
PENPOT_SMTP_USERNAME = ""
|
||||
PENPOT_SMTP_PASSWORD = ""
|
||||
PENPOT_SMTP_TLS = "false"
|
||||
PENPOT_SMTP_SSL = "false"
|
||||
}
|
||||
|
||||
job "penpot" {
|
||||
datacenters = ["dc1"]
|
||||
|
||||
group "frontend" {
|
||||
count = 1
|
||||
|
||||
network {
|
||||
mode = "bridge"
|
||||
port "ingress" {
|
||||
to = 80
|
||||
}
|
||||
}
|
||||
|
||||
# Expose frontend to internet through traefik
|
||||
service {
|
||||
name = "penpot"
|
||||
port = "ingress"
|
||||
|
||||
tags = [
|
||||
"traefik.enable=true",
|
||||
"traefik.http.routers.penpot.tls=true",
|
||||
"traefik.http.routers.penpot.entrypoints=websecure",
|
||||
"traefik.http.routers.penpot.rule=Host(`${local.TRAEFIK_DOMAIN}`)",
|
||||
]
|
||||
|
||||
connect {
|
||||
sidecar_service {
|
||||
proxy {
|
||||
upstreams {
|
||||
destination_name = "penpot-backend"
|
||||
local_bind_address = "127.0.0.1"
|
||||
local_bind_port = 6060
|
||||
}
|
||||
upstreams {
|
||||
destination_name = "penpot-exporter"
|
||||
local_bind_address = "127.0.0.1"
|
||||
local_bind_port = 6061
|
||||
}
|
||||
upstreams {
|
||||
destination_name = "minio"
|
||||
local_bind_address = "127.0.0.1"
|
||||
local_bind_port = 9000
|
||||
}
|
||||
}
|
||||
tags = ["traefik.enable=false"] # Hide service from traefik
|
||||
}
|
||||
}
|
||||
|
||||
check {
|
||||
type = "http"
|
||||
path = "/"
|
||||
interval = "10s"
|
||||
timeout = "2s"
|
||||
}
|
||||
}
|
||||
|
||||
task "frontend" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "penpotapp/frontend:2.0.1"
|
||||
ports = ["ingress"]
|
||||
}
|
||||
|
||||
env {
|
||||
PENPOT_PUBLIC_URI = local.PENPOT_PUBLIC_URI
|
||||
PENPOT_BACKEND_URI = local.PENPOT_BACKEND_URI
|
||||
PENPOT_EXPORTER_URI = local.PENPOT_EXPORTER_URI
|
||||
|
||||
PENPOT_FLAGS = local.PENPOT_FLAGS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group "backend" {
|
||||
|
||||
network {
|
||||
mode = "bridge"
|
||||
}
|
||||
|
||||
service {
|
||||
# Make available to other services by the 'penpot-backend' name
|
||||
name = "penpot-backend"
|
||||
port = "6060"
|
||||
tags = ["traefik.enable=false"] # Hide redis from traefik
|
||||
|
||||
# Make available through the consul service mesh
|
||||
connect {
|
||||
sidecar_service {
|
||||
proxy {
|
||||
upstreams {
|
||||
destination_name = "postgres"
|
||||
local_bind_address = "127.0.0.1"
|
||||
local_bind_port = 5432
|
||||
}
|
||||
upstreams {
|
||||
destination_name = "redis-cache"
|
||||
local_bind_address = "127.0.0.1"
|
||||
local_bind_port = 6379
|
||||
}
|
||||
upstreams {
|
||||
destination_name = "minio"
|
||||
local_bind_address = "127.0.0.1"
|
||||
local_bind_port = 9000
|
||||
}
|
||||
upstreams {
|
||||
destination_name = "fake-smtp"
|
||||
local_bind_address = "127.0.0.1"
|
||||
local_bind_port = 1025
|
||||
}
|
||||
}
|
||||
tags = ["traefik.enable=false"] # Hide penpot-backend envoy from traefik
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task "backend" {
|
||||
driver = "docker"
|
||||
config {
|
||||
image = "penpotapp/backend:2.0.1"
|
||||
ports = ["ingress"]
|
||||
}
|
||||
|
||||
env {
|
||||
PENPOT_PUBLIC_URI = local.PENPOT_PUBLIC_URI
|
||||
PENPOT_SECRET_KEY = local.PENPOT_SECRET_KEY
|
||||
PENPOT_DATABASE_URI = local.PENPOT_DATABASE_URI
|
||||
PENPOT_DATABASE_USERNAME = local.PENPOT_DATABASE_USERNAME
|
||||
PENPOT_DATABASE_PASSWORD = local.PENPOT_DATABASE_PASSWORD
|
||||
PENPOT_REDIS_URI = local.PENPOT_REDIS_URI
|
||||
PENPOT_FLAGS = local.PENPOT_FLAGS
|
||||
PENPOT_TELEMERY_ENABLED = local.PENPOT_TELEMERY_ENABLED
|
||||
|
||||
PENPOT_ASSETS_STORAGE_BACKEND = local.PENPOT_ASSETS_STORAGE_BACKEND
|
||||
PENPOT_STORAGE_ASSETS_FS_DIRECTORY = local.PENPOT_STORAGE_ASSETS_FS_DIRECTORY
|
||||
AWS_ACCESS_KEY_ID = local.AWS_ACCESS_KEY_ID
|
||||
AWS_SECRET_ACCESS_KEY = local.AWS_SECRET_ACCESS_KEY
|
||||
PENPOT_STORAGE_ASSETS_S3_ENDPOINT = local.PENPOT_STORAGE_ASSETS_S3_ENDPOINT
|
||||
PENPOT_STORAGE_ASSETS_S3_BUCKET = local.PENPOT_STORAGE_ASSETS_S3_BUCKET
|
||||
|
||||
PENPOT_SMTP_DEFAULT_FROM = local.PENPOT_SMTP_DEFAULT_FROM
|
||||
PENPOT_SMTP_DEFAULT_REPLY_TO = local.PENPOT_SMTP_DEFAULT_REPLY_TO
|
||||
PENPOT_SMTP_HOST = local.PENPOT_SMTP_HOST
|
||||
PENPOT_SMTP_PORT = local.PENPOT_SMTP_PORT
|
||||
PENPOT_SMTP_USERNAME = local.PENPOT_SMTP_USERNAME
|
||||
PENPOT_SMTP_PASSWORD = local.PENPOT_SMTP_PASSWORD
|
||||
PENPOT_SMTP_TLS = local.PENPOT_SMTP_TLS
|
||||
PENPOT_SMTP_SSL = local.PENPOT_SMTP_SSL
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 8000
|
||||
memory = 1024
|
||||
memory_max = 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group "exporter" {
|
||||
|
||||
network {
|
||||
mode = "bridge"
|
||||
}
|
||||
|
||||
task "exporter" {
|
||||
driver = "docker"
|
||||
config {
|
||||
image = "penpotapp/exporter:2.0.1"
|
||||
}
|
||||
|
||||
env {
|
||||
PENPOT_PUBLIC_URI = local.PENPOT_PUBLIC_URI
|
||||
PENPOT_REDIS_URI = local.PENPOT_REDIS_URI
|
||||
}
|
||||
}
|
||||
|
||||
service {
|
||||
name = "penpot-exporter"
|
||||
port = "6061"
|
||||
tags = ["traefik.enable=false"] # Hide envoy from traefik
|
||||
|
||||
connect {
|
||||
sidecar_service {
|
||||
proxy {
|
||||
upstreams {
|
||||
destination_name = "redis-cache"
|
||||
local_bind_address = "127.0.0.1"
|
||||
local_bind_port = 6379
|
||||
}
|
||||
}
|
||||
tags = ["traefik.enable=false"] # Hide envoy from traefik
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
nomad_jobs/services/penpot/readme.md
Normal file
21
nomad_jobs/services/penpot/readme.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Penpot
|
||||
Penpot is the Open-Source Design & Prototyping Tool for Product Teams. It is a great alternative to Figma or Adobe XD that you can host yourself. Learn all about it on their [website](https://penpot.app/).
|
||||
|
||||
## Nomad Job for Penpot
|
||||
Penpot already hosts documentation related to their architecture and how to deploy it; therefore, this will not duplicate documentation that can be found on their website such as available flags that can be applied. The nomad spec available in this repository defined the frontend, backend, and exporter services. It is expected that you already have the service dependencies running and available to the Penpot service as well as valid postgres credentials.
|
||||
|
||||
If you need help making those credentials, take a look at the [postgres readme](../postgres/readme.md#make-a-new-database).
|
||||
|
||||
## Service Dependencies
|
||||
- A Valid [Host Volume](../../../host_init/README.md#storage-and-zfs) or an S3 storage provider like [Minio](../minio/readme.md)
|
||||
- [Postgres](../postgres/readme.md)
|
||||
- [Redis Cache](../redis/readme.md)
|
||||
- *optional* - An SMTP server like [mailcatcher](../mailcatcher/readme.md) or a legitimate mail server
|
||||
|
||||
## Configuring Penpot
|
||||
Provided you do not need to make adjustments to the services exposed via the consul sidecar, the only edits needed are lines 1-42. The 'locals' blocks are referenced in other sections of the spec and as such we can do all of our configuration in one place. Some notable items are as follows:
|
||||
|
||||
- TRAEFIK_DOMAIN - The domain you will be registering with traefik for access to the application
|
||||
- PENPOT_FLAGS - remove 'enable_smtp' if you are not going to give SMTP configuration
|
||||
- PENPOT_STORAGE_BACKEND - The configurations are given for both file system and minio storage. It doesn't affect anything if you include environment variables for both but you must make sure that you have the storage backend set to either 'assests-fs' or 'assets-s3' to inform Penpot which you intend to use. If you are using a filesystem and wish to persist data, you will need to make sure that your host volumes are properly configured.
|
||||
|
||||
90
nomad_jobs/services/umami/umami.nomad.hcl
Normal file
90
nomad_jobs/services/umami/umami.nomad.hcl
Normal file
@@ -0,0 +1,90 @@
|
||||
# Listening Domain
|
||||
locals {
|
||||
SUBDOMAIN = "umami" // End with dot or leave blamk for root domain
|
||||
DOMAIN = "example.com"
|
||||
TRAEFIK_DOMAIN = "${local.SUBDOMAIN}${local.DOMAIN}"
|
||||
}
|
||||
|
||||
// OP is 1Password for CLI
|
||||
locals {
|
||||
OP_DB_USER = "op://InfraSecrets/Umami/ENV_SECRETS/PostgresUsername"
|
||||
OP_DB_PASSWORD = "op://InfraSecrets/Umami/ENV_SECRETS/PostgresPassword"
|
||||
OP_AppSecret = "op://InfraSecrets/Umami/ENV_SECRETS/AppSecret"
|
||||
}
|
||||
|
||||
locals {
|
||||
USER_PASSWORD = "${local.OP_DB_USER}:${local.OP_DB_PASSWORD}"
|
||||
|
||||
UMAMI_APPSECRET = "${local.OP_AppSecret}"
|
||||
UMAMI_DB_URL = "postgresql://${local.USER_PASSWORD}@127.0.0.1:5432/umami"
|
||||
UMAMI_DB_TYPE = "postgresql"
|
||||
}
|
||||
|
||||
job "umami" {
|
||||
datacenters = ["dc1"]
|
||||
type = "service"
|
||||
|
||||
group "application" {
|
||||
count = 1
|
||||
|
||||
network {
|
||||
mode = "bridge"
|
||||
port "httpIngress" {
|
||||
to = 3000
|
||||
}
|
||||
}
|
||||
|
||||
service {
|
||||
name = "umami"
|
||||
port = "httpIngress"
|
||||
|
||||
connect {
|
||||
sidecar_service {
|
||||
proxy {
|
||||
upstreams {
|
||||
destination_name = "postgres"
|
||||
local_bind_address = "127.0.0.1"
|
||||
local_bind_port = 5432
|
||||
}
|
||||
}
|
||||
tags = ["traefik.enable=false"] # Hide envoy from traefik
|
||||
}
|
||||
}
|
||||
|
||||
tags = [
|
||||
"traefik.enable=true",
|
||||
"traefik.http.routers.umami.tls=true",
|
||||
"traefik.http.routers.umami.entrypoints=websecure",
|
||||
"traefik.http.routers.umami.rule=Host(`${local.TRAEFIK_DOMAIN}`)"
|
||||
]
|
||||
|
||||
check {
|
||||
type = "http"
|
||||
path = "/api/heartbeat"
|
||||
interval = "10s"
|
||||
timeout = "2s"
|
||||
}
|
||||
}
|
||||
|
||||
task "umami" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "ghcr.io/umami-software/umami:postgresql-latest"
|
||||
ports = ["httpIngress"]
|
||||
}
|
||||
|
||||
env = {
|
||||
DATABASE_URL="${local.UMAMI_DB_URL}"
|
||||
DATABASE_TYPE="${local.UMAMI_DB_TYPE}"
|
||||
APP_SECRET:"${local.UMAMI_APPSECRET}"
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 1000
|
||||
memory = 512
|
||||
memory_max = 1024
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
- name: Update consul config
|
||||
ansible.builtin.copy:
|
||||
mode: preserve
|
||||
src: ./host_config/consul.hcl
|
||||
src: ../host_config/consul.hcl
|
||||
dest: /etc/consul/server.hcl
|
||||
|
||||
- name: Restart consul service
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
- name: Update nomad config
|
||||
ansible.builtin.copy:
|
||||
mode: preserve
|
||||
src: ./host_config/nomad.hcl
|
||||
src: ../host_config/nomad.hcl
|
||||
dest: /etc/nomad.d/server.hcl
|
||||
|
||||
- name: Restart nomad service
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
- name: Update traefik config
|
||||
ansible.builtin.copy:
|
||||
mode: preserve
|
||||
src: ./host_config/traefik.yml
|
||||
src: ../host_config/traefik.yml
|
||||
dest: /etc/traefik/traefik.yaml # Alpine default config is yaml
|
||||
|
||||
- name: Restart traefik service
|
||||
|
||||
Reference in New Issue
Block a user