Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions ct/openseo.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/usr/bin/env bash
_cs_boot="${COMMUNITY_SCRIPTS_CORE_DIR:-$(dirname "${BASH_SOURCE[0]}")/../../core}/core/build.func"
source "$_cs_boot" 2>/dev/null || source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/core/build.func")
# Copyright (c) 2021-2026 community-scripts ORG
# Author: Brandon Visca (visbran)
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
# Source: https://github.com/every-app/open-seo

APP="OpenSEO"
var_tags="${var_tags:-seo;analytics;marketing}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-4096}"
var_disk="${var_disk:-10}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
#var_arm64="${var_arm64:-no}" # unset = ask the user; set yes/no only when verified
var_unprivileged="${var_unprivileged:-1}"
var_testurl="${var_testurl:-https://github.com/community-scripts/ProxmoxVED/issues/2324}"
export var_dataforseo_api_key="${var_dataforseo_api_key:-}"

header_info "$APP"
variables
color
catch_errors

function update_script() {
header_info
check_container_storage
check_container_resources

if [[ ! -d /opt/openseo ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi

if check_for_gh_release "openseo" "every-app/open-seo"; then
msg_info "Stopping OpenSEO"
systemctl stop openseo
msg_ok "Stopped OpenSEO"

create_backup /opt/openseo/.env /opt/openseo/.wrangler

CLEAN_INSTALL=1 fetch_and_deploy_gh_release "openseo" "every-app/open-seo" "tarball"

restore_backup

PNPM_VERSION=$(sed -n 's/.*"packageManager": "pnpm@\([^"+]*\).*/\1/p' /opt/openseo/package.json)
NODE_VERSION="22" NODE_MODULE="pnpm@${PNPM_VERSION:-10.30.1}" setup_nodejs

msg_info "Building OpenSEO"
cd /opt/openseo
$STD pnpm install --frozen-lockfile
# The Cloudflare vite plugin serializes the whole process env into .dev.vars; run with a clean env so it only sees /opt/openseo/.env
$STD env -i PATH="$PATH" HOME=/root pnpm run db:migrate:local
$STD env -i PATH="$PATH" HOME=/root pnpm run build
msg_ok "Built OpenSEO"

msg_info "Starting OpenSEO"
systemctl start openseo
msg_ok "Started OpenSEO"
msg_ok "Updated successfully!"
fi
exit
}

start
build_container
description

msg_ok "Completed Successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3001${CL}"
70 changes: 70 additions & 0 deletions install/openseo-install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env bash

# Copyright (c) 2021-2026 community-scripts ORG
# Author: Brandon Visca (visbran)
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
# Source: https://github.com/every-app/open-seo

source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os

fetch_and_deploy_gh_release "openseo" "every-app/open-seo" "tarball"

PNPM_VERSION=$(sed -n 's/.*"packageManager": "pnpm@\([^"+]*\).*/\1/p' /opt/openseo/package.json)
NODE_VERSION="22" NODE_MODULE="pnpm@${PNPM_VERSION:-10.30.1}" setup_nodejs

if [[ -z "${var_dataforseo_api_key:-}" ]]; then
var_dataforseo_api_key=$(prompt_input "DataForSEO API key (base64 of login:password, leave empty to set later):" "" 120)
fi

msg_info "Configuring OpenSEO"
cat <<EOF >/opt/openseo/.env
AUTH_MODE=local_noauth
CLOUDFLARE_INCLUDE_PROCESS_ENV=true
PORT=3001
ALLOWED_HOST=
DATAFORSEO_API_KEY=${var_dataforseo_api_key:-}
OPENROUTER_API_KEY=
OPENROUTER_MODEL=
OPENSEO_TELEMETRY_DISABLED=
VITE_SHOW_DEVTOOLS=false
EOF
msg_ok "Configured OpenSEO"

msg_info "Building OpenSEO"
cd /opt/openseo
$STD pnpm install --frozen-lockfile
# The Cloudflare vite plugin serializes the whole process env into .dev.vars; run with a clean env so it only sees /opt/openseo/.env
$STD env -i PATH="$PATH" HOME=/root pnpm run db:migrate:local
$STD env -i PATH="$PATH" HOME=/root pnpm run build
msg_ok "Built OpenSEO"

msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/openseo.service
[Unit]
Description=OpenSEO
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/opt/openseo
EnvironmentFile=/opt/openseo/.env
ExecStart=/usr/bin/pnpm exec vite preview --host 0.0.0.0
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now openseo
msg_ok "Created Service"

motd_ssh
customize
cleanup_lxc
67 changes: 67 additions & 0 deletions json/openseo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"name": "OpenSEO",
"slug": "openseo",
"categories": [
9
],
"date_created": "2026-09-27",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 3001,
"documentation": "https://github.com/every-app/open-seo/tree/main/docs",
"website": "https://openseo.so/",
"repository": "https://github.com/every-app/open-seo",
"logo": "https://cdn.jsdelivr.net/gh/every-app/open-seo@main/public/android-chrome-192x192.png",
"description": "OpenSEO is an open source SEO platform for keyword research, rank tracking, competitor analysis, backlinks and site audits, powered by the pay-as-you-go DataForSEO API.",
"install_methods": [
{
"type": "default",
"script": "ct/openseo.sh",
"config_path": "/opt/openseo/.env",
"resources": {
"cpu": 2,
"ram": 4096,
"hdd": 10,
"os": "Debian",
"version": "13"
}
}
],
"default_credentials": {
"username": null,
"password": null
},
"app_vars": [
{
"name": "var_dataforseo_api_key",
"label": "DataForSEO API Key",
"type": "password",
"secret": true,
"required": false,
"help": "Base64 of your DataForSEO login:password (printf 'email:password' | base64). Can be set later in /opt/openseo/.env."
}
],
"notes": [
{
"text": "OpenSEO runs with AUTH_MODE=local_noauth: there is no login, anyone who reaches port 3001 is the admin. Keep it on a private network or behind an auth-protected reverse proxy.",
"type": "warning"
},
{
"text": "SEO data comes from the paid DataForSEO API (pay-as-you-go, $50 minimum top-up). DATAFORSEO_API_KEY is the base64 of your DataForSEO login:password.",
"type": "info"
},
{
"text": "The configuration is baked in at build time: after editing /opt/openseo/.env (DATAFORSEO_API_KEY, ALLOWED_HOST, OPENSEO_TELEMETRY_DISABLED...), a restart is not enough. Rebuild with 'cd /opt/openseo && env -i PATH=\"$PATH\" HOME=/root pnpm run build && systemctl restart openseo'.",
"type": "warning"
},
{
"text": "Behind a reverse proxy, set ALLOWED_HOST to the public hostname in /opt/openseo/.env, then rebuild.",
"type": "info"
},
{
"text": "OpenSEO sends an anonymous usage heartbeat. Set OPENSEO_TELEMETRY_DISABLED=1 in /opt/openseo/.env, then rebuild, to turn it off.",
"type": "info"
}
]
}
Loading