diff --git a/apps/public/content/docs/self-hosting/self-hosting.mdx b/apps/public/content/docs/self-hosting/self-hosting.mdx
index 524aab47..eee2a785 100644
--- a/apps/public/content/docs/self-hosting/self-hosting.mdx
+++ b/apps/public/content/docs/self-hosting/self-hosting.mdx
@@ -16,7 +16,7 @@ import { Step, Steps } from 'fumadocs-ui/components/steps';
### Quickstart
```bash
-git clone https://github.com/Openpanel-dev/openpanel && cd openpanel/self-hosting && ./setup
+git clone -b self-hosting https://github.com/Openpanel-dev/openpanel && cd openpanel/self-hosting && ./setup
# After setup is complete run `./start` to start OpenPanel
```
@@ -25,10 +25,10 @@ git clone https://github.com/Openpanel-dev/openpanel && cd openpanel/self-hostin
### Clone
-Clone the repository to your VPS
+Clone the repository to your VPS and checkout the self-hosting tag
```bash
-git clone https://github.com/Openpanel-dev/openpanel.git
+git clone -b self-hosting https://github.com/Openpanel-dev/openpanel.git
```
diff --git a/apps/worker/src/boot-cron.ts b/apps/worker/src/boot-cron.ts
index e9869081..30661e06 100644
--- a/apps/worker/src/boot-cron.ts
+++ b/apps/worker/src/boot-cron.ts
@@ -36,10 +36,7 @@ export async function bootCron() {
},
];
- if (
- (process.env.VITE_SELF_HOSTED === 'true' || process.env.SELF_HOSTED) &&
- process.env.NODE_ENV === 'production'
- ) {
+ if (process.env.SELF_HOSTED && process.env.NODE_ENV === 'production') {
jobs.push({
name: 'ping',
type: 'ping',
diff --git a/apps/worker/src/jobs/cron.delete-projects.ts b/apps/worker/src/jobs/cron.delete-projects.ts
index da82341e..abb8f684 100644
--- a/apps/worker/src/jobs/cron.delete-projects.ts
+++ b/apps/worker/src/jobs/cron.delete-projects.ts
@@ -47,7 +47,7 @@ export async function deleteProjects(job: Job) {
for (const table of tables) {
const query =
- process.env.VITE_SELF_HOSTED === 'true'
+ process.env.SELF_HOSTED === 'true'
? `ALTER TABLE ${table} DELETE WHERE ${where};`
: `ALTER TABLE ${table}_replicated ON CLUSTER '{cluster}' DELETE WHERE ${where};`;
diff --git a/packages/db/code-migrations/helpers.ts b/packages/db/code-migrations/helpers.ts
index 59b35307..1c095365 100644
--- a/packages/db/code-migrations/helpers.ts
+++ b/packages/db/code-migrations/helpers.ts
@@ -24,7 +24,7 @@ export function getIsCluster() {
}
export function getIsSelfHosting() {
- return process.env.VITE_SELF_HOSTED === 'true' || !!process.env.SELF_HOSTED;
+ return process.env.SELF_HOSTED === 'true' || !!process.env.SELF_HOSTED;
}
export function getIsDry() {
diff --git a/packages/db/src/prisma-client.ts b/packages/db/src/prisma-client.ts
index e7e2d86a..6943e4ec 100644
--- a/packages/db/src/prisma-client.ts
+++ b/packages/db/src/prisma-client.ts
@@ -59,7 +59,7 @@ const getPrismaClient = () => {
subscriptionStatus: {
needs: { subscriptionStatus: true, subscriptionCanceledAt: true },
compute(org) {
- if (process.env.VITE_SELF_HOSTED === 'true') {
+ if (process.env.SELF_HOSTED === 'true') {
return 'active';
}
@@ -69,7 +69,7 @@ const getPrismaClient = () => {
hasSubscription: {
needs: { subscriptionStatus: true, subscriptionEndsAt: true },
compute(org) {
- if (process.env.VITE_SELF_HOSTED === 'true') {
+ if (process.env.SELF_HOSTED === 'true') {
return false;
}
@@ -94,7 +94,7 @@ const getPrismaClient = () => {
subscriptionPeriodEventsCountExceededAt: true,
},
compute(org) {
- if (process.env.VITE_SELF_HOSTED === 'true') {
+ if (process.env.SELF_HOSTED === 'true') {
return null;
}
@@ -131,7 +131,7 @@ const getPrismaClient = () => {
isCanceled: {
needs: { subscriptionStatus: true, subscriptionCanceledAt: true },
compute(org) {
- if (process.env.VITE_SELF_HOSTED === 'true') {
+ if (process.env.SELF_HOSTED === 'true') {
return false;
}
@@ -145,7 +145,7 @@ const getPrismaClient = () => {
subscriptionEndsAt: true,
},
compute(org) {
- if (process.env.VITE_SELF_HOSTED === 'true') {
+ if (process.env.SELF_HOSTED === 'true') {
return false;
}
@@ -159,7 +159,7 @@ const getPrismaClient = () => {
subscriptionCanceledAt: true,
},
compute(org) {
- if (process.env.VITE_SELF_HOSTED === 'true') {
+ if (process.env.SELF_HOSTED === 'true') {
return false;
}
@@ -182,7 +182,7 @@ const getPrismaClient = () => {
subscriptionPeriodEventsLimit: true,
},
compute(org) {
- if (process.env.VITE_SELF_HOSTED === 'true') {
+ if (process.env.SELF_HOSTED === 'true') {
return false;
}
@@ -195,7 +195,7 @@ const getPrismaClient = () => {
subscriptionCurrentPeriodStart: {
needs: { subscriptionStartsAt: true, subscriptionInterval: true },
compute(org) {
- if (process.env.VITE_SELF_HOSTED === 'true') {
+ if (process.env.SELF_HOSTED === 'true') {
return null;
}
@@ -229,7 +229,7 @@ const getPrismaClient = () => {
subscriptionInterval: true,
},
compute(org) {
- if (process.env.VITE_SELF_HOSTED === 'true') {
+ if (process.env.SELF_HOSTED === 'true') {
return null;
}
diff --git a/packages/trpc/src/routers/onboarding.ts b/packages/trpc/src/routers/onboarding.ts
index 59e14373..071b5a7e 100644
--- a/packages/trpc/src/routers/onboarding.ts
+++ b/packages/trpc/src/routers/onboarding.ts
@@ -33,7 +33,7 @@ async function createOrGetOrganization(
},
});
- if (process.env.VITE_SELF_HOSTED !== 'true' && !process.env.SELF_HOSTED) {
+ if (!process.env.SELF_HOSTED) {
await addTrialEndingSoonJob(
organization.id,
1000 * 60 * 60 * 24 * TRIAL_DURATION_IN_DAYS * 0.9,
diff --git a/self-hosting/coolify.yml b/self-hosting/coolify.yml
index b40029b8..521143a4 100644
--- a/self-hosting/coolify.yml
+++ b/self-hosting/coolify.yml
@@ -135,7 +135,7 @@ services:
environment:
# Common
- NODE_ENV=production
- - VITE_SELF_HOSTED=true
+ - SELF_HOSTED=true
# URLs
- DATABASE_URL=postgres://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@opdb:5432/${OPENPANEL_POSTGRES_DB:-openpanel-db}?schema=public
- DATABASE_URL_DIRECT=postgres://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@opdb:5432/${OPENPANEL_POSTGRES_DB:-openpanel-db}?schema=public
@@ -166,7 +166,7 @@ services:
environment:
# Common
- NODE_ENV=production
- - VITE_SELF_HOSTED=true
+ - SELF_HOSTED=true
# URLs
- DATABASE_URL=postgres://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@opdb:5432/${OPENPANEL_POSTGRES_DB:-openpanel-db}?schema=public
- REDIS_URL=redis://default:${SERVICE_PASSWORD_REDIS}@opkv:6379
@@ -193,7 +193,7 @@ services:
- SERVICE_FQDN_OPBULLBOARD
# Common
- NODE_ENV=production
- - VITE_SELF_HOSTED=true
+ - SELF_HOSTED=true
# URLs
- DATABASE_URL=postgres://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@opdb:5432/${OPENPANEL_POSTGRES_DB:-openpanel-db}?schema=public
- DATABASE_URL_DIRECT=postgres://${SERVICE_USER_POSTGRES}:${SERVICE_PASSWORD_POSTGRES}@opdb:5432/${OPENPANEL_POSTGRES_DB:-openpanel-db}?schema=public
diff --git a/self-hosting/get_latest_images b/self-hosting/get_latest_images
index 765d6c36..4744965a 100755
--- a/self-hosting/get_latest_images
+++ b/self-hosting/get_latest_images
@@ -77,6 +77,65 @@ while [[ $# -gt 0 ]]; do
esac
done
+# Check if jq is installed (required for JSON parsing)
+if ! command -v jq &> /dev/null; then
+ echo -e "${YELLOW}jq is required but not installed${NC}\n"
+ echo -e "${CYAN}jq is a lightweight JSON processor needed to parse GitHub API responses.${NC}\n"
+
+ # Detect OS and suggest installation
+ if [[ -f /etc/os-release ]]; then
+ . /etc/os-release
+ OS_ID=$ID
+ elif [[ "$OSTYPE" == "darwin"* ]]; then
+ OS_ID="macos"
+ else
+ OS_ID="unknown"
+ fi
+
+ # Determine installation command
+ case $OS_ID in
+ ubuntu|debian)
+ INSTALL_CMD="sudo apt-get update && sudo apt-get install -y jq"
+ ;;
+ rhel|centos|fedora)
+ INSTALL_CMD="sudo yum install -y jq"
+ ;;
+ alpine)
+ INSTALL_CMD="sudo apk add --no-cache jq"
+ ;;
+ macos)
+ INSTALL_CMD="brew install jq"
+ ;;
+ *)
+ echo -e "${RED}Could not detect your OS.${NC}"
+ echo -e "${YELLOW}Please install jq manually:${NC}"
+ echo -e " ${GREEN}Ubuntu/Debian:${NC} sudo apt-get update && sudo apt-get install -y jq"
+ echo -e " ${GREEN}RHEL/CentOS:${NC} sudo yum install -y jq"
+ echo -e " ${GREEN}Alpine:${NC} sudo apk add --no-cache jq"
+ echo -e " ${GREEN}macOS:${NC} brew install jq"
+ echo ""
+ exit 1
+ ;;
+ esac
+
+ # Ask user if they want to install
+ read -p "$(echo -e ${GREEN}Would you like to install jq now? [Y/n]:${NC} )" -n 1 -r
+ echo
+ if [[ $REPLY =~ ^[Nn]$ ]]; then
+ echo -e "${RED}jq is required to continue. Please install it manually.${NC}"
+ exit 1
+ fi
+
+ # Install jq
+ echo -e "${BLUE}Installing jq...${NC}\n"
+ if eval "$INSTALL_CMD"; then
+ echo -e "\n${GREEN}✓ jq installed successfully!${NC}\n"
+ else
+ echo -e "\n${RED}✗ Failed to install jq. Please install it manually.${NC}"
+ exit 1
+ fi
+fi
+
# Check if user needs to be logged in (for apply mode)
if [ "$APPLY_MODE" = true ]; then
# Check if Docker is available
@@ -135,11 +194,7 @@ fi
# List all tags if requested
if [ "$LIST_ALL" = true ]; then
echo -e "${GREEN}All available tags:${NC}\n"
- if command -v jq &> /dev/null; then
- echo "$TAGS_JSON" | jq -r '.[] | " \(.name) (\(.commit.sha[0:7]))"'
- else
- echo "$TAGS_JSON" | grep "\"name\":" | sed 's/.*"name": "\([^"]*\)".*/ \1/'
- fi
+ echo "$TAGS_JSON" | jq -r '.[] | " \(.name) (\(.commit.sha[0:7]))"'
echo ""
exit 0
fi
@@ -150,15 +205,9 @@ get_latest_tag() {
local output_var_tag=$2
local output_var_sha=$3
- if command -v jq &> /dev/null; then
- # Use jq for better JSON parsing
- local tag=$(echo "$TAGS_JSON" | jq -r "[.[] | select(.name | contains(\"${component}\"))] | .[0] | .name" 2>/dev/null)
- local sha=$(echo "$TAGS_JSON" | jq -r "[.[] | select(.name | contains(\"${component}\"))] | .[0] | .commit.sha" 2>/dev/null)
- else
- # Fallback to grep/sed
- local tag=$(echo "$TAGS_JSON" | grep -o "\"name\": \"[^\"]*${component}[^\"]*\"" | head -1 | cut -d'"' -f4)
- local sha=$(echo "$TAGS_JSON" | grep -B5 "\"name\": \"${tag}\"" | grep "\"sha\"" | head -1 | cut -d'"' -f4)
- fi
+ # Use jq for JSON parsing
+ local tag=$(echo "$TAGS_JSON" | jq -r "[.[] | select(.name | contains(\"${component}\"))] | .[0] | .name" 2>/dev/null)
+ local sha=$(echo "$TAGS_JSON" | jq -r "[.[] | select(.name | contains(\"${component}\"))] | .[0] | .commit.sha" 2>/dev/null)
if [ -z "$tag" ] || [ "$tag" == "null" ]; then
echo -e "${RED}✗${NC} ${component}: No matching tag found"
diff --git a/sh/docker-build b/sh/docker-build
index c95e0797..45509b4c 100755
--- a/sh/docker-build
+++ b/sh/docker-build
@@ -46,7 +46,6 @@ build_image() {
--platform linux/amd64,linux/arm64 \
-t "$full_version" \
--build-arg DATABASE_URL="postgresql://p@p:5432/p" \
- --build-arg VITE_SELF_HOSTED="true" \
-f "apps/$app/Dockerfile" \
--push \
.
@@ -57,7 +56,6 @@ build_image() {
-t "$full_version" \
-t "$image_name:latest" \
--build-arg DATABASE_URL="postgresql://p@p:5432/p" \
- --build-arg VITE_SELF_HOSTED="true" \
-f "apps/$app/Dockerfile" \
--push \
.
diff --git a/sh/tag-self-hosting b/sh/tag-self-hosting
new file mode 100755
index 00000000..299278e0
--- /dev/null
+++ b/sh/tag-self-hosting
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# Tags to manage
+TAGS=("self-hosting")
+
+# Get commit from argument or default to HEAD
+COMMIT="${1:-HEAD}"
+
+echo "🏷️ Managing tags: ${TAGS[@]}"
+echo "📍 Target commit: $COMMIT"
+echo ""
+
+# Verify commit exists
+if ! git rev-parse --verify "$COMMIT" >/dev/null 2>&1; then
+ echo "❌ Error: Invalid commit reference: $COMMIT"
+ exit 1
+fi
+
+# Delete local tags
+echo "🗑️ Deleting local tags..."
+for tag in "${TAGS[@]}"; do
+ if git tag -l "$tag" | grep -q "$tag"; then
+ git tag -d "$tag"
+ echo " ✓ Deleted local tag: $tag"
+ else
+ echo " - Tag $tag doesn't exist locally"
+ fi
+done
+
+echo ""
+
+# Delete remote tags
+echo "🗑️ Deleting remote tags..."
+for tag in "${TAGS[@]}"; do
+ if git ls-remote --tags origin | grep -q "refs/tags/$tag"; then
+ SKIP_HOOKS=1 git push origin ":refs/tags/$tag" 2>/dev/null
+ echo " ✓ Deleted remote tag: $tag"
+ else
+ echo " - Tag $tag doesn't exist on remote"
+ fi
+done
+
+echo ""
+
+# Create new tags
+echo "🏷️ Creating new tags on commit $COMMIT..."
+for tag in "${TAGS[@]}"; do
+ git tag "$tag" "$COMMIT"
+ echo " ✓ Created tag: $tag"
+done
+
+echo ""
+
+# Push tags
+echo "🚀 Pushing tags to remote..."
+SKIP_HOOKS=1 git push origin "${TAGS[@]}"
+
+echo ""
+echo "✅ Done! Tags updated successfully."
+
diff --git a/sh/tag-supporter b/sh/tag-supporter
new file mode 100755
index 00000000..71a1041f
--- /dev/null
+++ b/sh/tag-supporter
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# Tags to manage
+TAGS=("api" "worker" "dashboard")
+
+# Get commit from argument or default to HEAD
+COMMIT="${1:-HEAD}"
+
+echo "🏷️ Managing tags: ${TAGS[@]}"
+echo "📍 Target commit: $COMMIT"
+echo ""
+
+# Verify commit exists
+if ! git rev-parse --verify "$COMMIT" >/dev/null 2>&1; then
+ echo "❌ Error: Invalid commit reference: $COMMIT"
+ exit 1
+fi
+
+# Delete local tags
+echo "🗑️ Deleting local tags..."
+for tag in "${TAGS[@]}"; do
+ if git tag -l "$tag" | grep -q "$tag"; then
+ git tag -d "$tag"
+ echo " ✓ Deleted local tag: $tag"
+ else
+ echo " - Tag $tag doesn't exist locally"
+ fi
+done
+
+echo ""
+
+# Delete remote tags
+echo "🗑️ Deleting remote tags..."
+for tag in "${TAGS[@]}"; do
+ if git ls-remote --tags origin | grep -q "refs/tags/$tag"; then
+ SKIP_HOOKS=1 git push origin ":refs/tags/$tag" 2>/dev/null
+ echo " ✓ Deleted remote tag: $tag"
+ else
+ echo " - Tag $tag doesn't exist on remote"
+ fi
+done
+
+echo ""
+
+# Create new tags
+echo "🏷️ Creating new tags on commit $COMMIT..."
+for tag in "${TAGS[@]}"; do
+ git tag "$tag" "$COMMIT"
+ echo " ✓ Created tag: $tag"
+done
+
+echo ""
+
+# Push tags
+echo "🚀 Pushing tags to remote..."
+SKIP_HOOKS=1 git push origin "${TAGS[@]}"
+
+echo ""
+echo "✅ Done! Tags updated successfully."
+