fix(docker-usage): single image setup

This commit is contained in:
realies
2025-03-08 04:41:27 +00:00
parent b3b8a05bd2
commit a661450633
20 changed files with 240 additions and 203 deletions

View File

@ -10,9 +10,6 @@ on:
jobs:
build-and-push:
runs-on: ubuntu-latest
strategy:
matrix:
service: [backend, app]
steps:
- name: Checkout code
uses: actions/checkout@v3
@ -36,38 +33,24 @@ jobs:
id: version
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
- name: Build and push Docker image for ${{ matrix.service }}
- name: Build and push Docker image (latest)
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
run: |
docker buildx create --use
if [[ "${{ matrix.service }}" == "backend" ]]; then \
DOCKERFILE=backend.dockerfile; \
IMAGE_NAME=perplexica-backend; \
else \
DOCKERFILE=app.dockerfile; \
IMAGE_NAME=perplexica-frontend; \
fi
docker buildx build --platform linux/amd64,linux/arm64 \
--cache-from=type=registry,ref=itzcrazykns1337/${IMAGE_NAME}:main \
docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 \
--cache-from=type=registry,ref=itzcrazykns1337/perplexica:latest \
--cache-to=type=inline \
-f $DOCKERFILE \
-t itzcrazykns1337/${IMAGE_NAME}:main \
-f docker/Dockerfile \
-t itzcrazykns1337/perplexica:latest \
--push .
- name: Build and push release Docker image for ${{ matrix.service }}
- name: Build and push Docker image (release)
if: github.event_name == 'release'
run: |
docker buildx create --use
if [[ "${{ matrix.service }}" == "backend" ]]; then \
DOCKERFILE=backend.dockerfile; \
IMAGE_NAME=perplexica-backend; \
else \
DOCKERFILE=app.dockerfile; \
IMAGE_NAME=perplexica-frontend; \
fi
docker buildx build --platform linux/amd64,linux/arm64 \
--cache-from=type=registry,ref=itzcrazykns1337/${IMAGE_NAME}:${{ env.RELEASE_VERSION }} \
docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 \
--cache-from=type=registry,ref=itzcrazykns1337/perplexica:${{ env.RELEASE_VERSION }} \
--cache-to=type=inline \
-f $DOCKERFILE \
-t itzcrazykns1337/${IMAGE_NAME}:${{ env.RELEASE_VERSION }} \
-f docker/Dockerfile \
-t itzcrazykns1337/perplexica:${{ env.RELEASE_VERSION }} \
--push .

View File

@ -26,7 +26,7 @@
- [Preview](#preview)
- [Features](#features)
- [Installation](#installation)
- [Getting Started with Docker (Recommended)](#getting-started-with-docker-recommended)
- [Docker Installation (Recommended)](#docker-installation-recommended)
- [Non-Docker Installation](#non-docker-installation)
- [Nginx Reverse Proxy](#nginx-reverse-proxy)
- [Ollama Connection Errors](#ollama-connection-errors)
@ -72,9 +72,9 @@ It has many more features like image and video search. Some of the planned featu
## Installation
There are mainly 2 ways of installing Perplexica - With Docker, Without Docker. Using Docker is highly recommended.
Perplexica can be installed using Docker (recommended) or directly on your system.
### Getting Started with Docker (Recommended)
### Docker Installation (Recommended)
1. Ensure Docker is installed and running on your system.
2. Clone the Perplexica repository:
@ -102,10 +102,15 @@ There are mainly 2 ways of installing Perplexica - With Docker, Without Docker.
docker compose up -d
```
6. Wait a few minutes for the setup to complete. You can access Perplexica at http://localhost:3000 in your web browser.
6. Wait a few minutes for the setup to complete. You can access Perplexica at http://localhost:8080 in your web browser.
**Note**: After the containers are built, you can start Perplexica directly from Docker without having to open a terminal.
The Docker configuration is located in the `docker/` directory, containing:
- Dockerfile with multi-stage build for efficient images
- Service configurations for the integrated process manager
- Nginx reverse proxy configuration
### Non-Docker Installation
1. Install SearXNG and allow `JSON` format in the SearXNG settings.

View File

@ -1,15 +0,0 @@
FROM node:20.18.0-alpine
ARG NEXT_PUBLIC_WS_URL=ws://127.0.0.1:3001
ARG NEXT_PUBLIC_API_URL=http://127.0.0.1:3001/api
ENV NEXT_PUBLIC_WS_URL=${NEXT_PUBLIC_WS_URL}
ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
WORKDIR /home/perplexica
COPY ui /home/perplexica/
RUN yarn install --frozen-lockfile
RUN yarn build
CMD ["yarn", "start"]

View File

@ -1,17 +0,0 @@
FROM node:18-slim
WORKDIR /home/perplexica
COPY src /home/perplexica/src
COPY tsconfig.json /home/perplexica/
COPY drizzle.config.ts /home/perplexica/
COPY package.json /home/perplexica/
COPY yarn.lock /home/perplexica/
RUN mkdir /home/perplexica/data
RUN mkdir /home/perplexica/uploads
RUN yarn install --frozen-lockfile --network-timeout 600000
RUN yarn build
CMD ["yarn", "start"]

View File

@ -2,62 +2,43 @@ services:
searxng:
image: docker.io/searxng/searxng:latest
volumes:
- ./searxng:/etc/searxng:rw
ports:
- 4000:8080
- ./searxng:/etc/searxng
networks:
- perplexica-network
restart: unless-stopped
perplexica-backend:
build:
context: .
dockerfile: backend.dockerfile
image: itzcrazykns1337/perplexica-backend:main
environment:
- SEARXNG_API_URL=http://searxng:8080
depends_on:
- searxng
perplexica:
image: itzcrazykns1337/perplexica:latest
ports:
- 3001:3001
- "8080:8080"
environment:
- SEARXNG_API_URL=http://searxng:4000
- SIMILARITY_MEASURE=cosine
- KEEP_ALIVE=5m
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
- GROQ_API_KEY=${GROQ_API_KEY:-}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
- GEMINI_API_KEY=${GEMINI_API_KEY:-}
- OLLAMA_API_URL=${OLLAMA_API_URL:-}
- CUSTOM_OPENAI_API_KEY=${CUSTOM_OPENAI_API_KEY:-}
- CUSTOM_OPENAI_API_URL=${CUSTOM_OPENAI_API_URL:-}
- CUSTOM_OPENAI_MODEL_NAME=${CUSTOM_OPENAI_MODEL_NAME:-}
volumes:
- backend-dbstore:/home/perplexica/data
- uploads:/home/perplexica/uploads
- ./config.toml:/home/perplexica/config.toml
- backend-dbstore:/app/backend/data
- uploads:/app/backend/uploads
extra_hosts:
- 'host.docker.internal:host-gateway'
networks:
- perplexica-network
restart: unless-stopped
perplexica-frontend:
build:
context: .
dockerfile: app.dockerfile
args:
- NEXT_PUBLIC_API_URL=/api
- NEXT_PUBLIC_WS_URL=auto
image: itzcrazykns1337/perplexica-frontend:main
depends_on:
- perplexica-backend
ports:
- 3000:3000
networks:
- perplexica-network
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- perplexica-frontend
- perplexica-backend
- searxng
networks:
- perplexica-network
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 5s
networks:
perplexica-network:

93
docker/Dockerfile Normal file
View File

@ -0,0 +1,93 @@
# Multi-stage build for Perplexica
# Stage 1: Build the backend
FROM node:lts-alpine as backend-builder
WORKDIR /app
COPY src ./src
COPY tsconfig.json drizzle.config.ts package.json yarn.lock ./
RUN yarn install --frozen-lockfile --network-timeout 600000 && \
yarn build
# Stage 2: Build the frontend
FROM node:lts-alpine as frontend-builder
WORKDIR /app
COPY ui ./
ARG NEXT_PUBLIC_API_URL=/api
ARG NEXT_PUBLIC_WS_URL=auto
ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
ENV NEXT_PUBLIC_WS_URL=${NEXT_PUBLIC_WS_URL}
RUN yarn install --frozen-lockfile && \
yarn build
# Stage 3: Final image
FROM node:lts-alpine
# Install curl and jq for GitHub API access
RUN apk add --no-cache curl jq
# Determine latest S6 overlay version at build time
RUN S6_OVERLAY_VERSION=$(curl -s https://api.github.com/repos/just-containers/s6-overlay/releases/latest | jq -r .tag_name | sed 's/^v//') && \
echo "Using S6 overlay version: $S6_OVERLAY_VERSION" && \
echo "$S6_OVERLAY_VERSION" > /tmp/s6-version
# Use Docker's TARGETARCH for automatic architecture detection
ARG TARGETARCH
# Install additional required packages and create directory structure in one layer
RUN apk add --no-cache \
nginx \
tzdata \
bash && \
mkdir -p /app/backend /app/frontend /app/data /app/uploads
# Map Docker's architecture names to s6-overlay architecture names and download/install
RUN S6_OVERLAY_VERSION=$(cat /tmp/s6-version) && \
case "${TARGETARCH}" in \
"amd64") S6_OVERLAY_ARCH="x86_64" ;; \
"arm64") S6_OVERLAY_ARCH="aarch64" ;; \
"arm") S6_OVERLAY_ARCH="arm" ;; \
*) echo "Unsupported architecture: ${TARGETARCH}. Only amd64, arm64, and arm are supported." && exit 1 ;; \
esac && \
echo "Target architecture: ${TARGETARCH} -> S6 architecture: ${S6_OVERLAY_ARCH}" && \
echo "Downloading s6-overlay v${S6_OVERLAY_VERSION} for architecture: ${S6_OVERLAY_ARCH}" && \
curl -L -s -o /tmp/s6-overlay-noarch.tar.xz "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz" && \
tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz && \
curl -L -s -o /tmp/s6-overlay-arch.tar.xz "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-${S6_OVERLAY_ARCH}.tar.xz" && \
tar -C / -Jxpf /tmp/s6-overlay-arch.tar.xz && \
curl -L -s -o /tmp/s6-overlay-symlinks-noarch.tar.xz "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-symlinks-noarch.tar.xz" && \
tar -C / -Jxpf /tmp/s6-overlay-symlinks-noarch.tar.xz && \
rm -f /tmp/s6-overlay-*.tar.xz /tmp/s6-version
# Copy configuration files
COPY docker/etc/s6-overlay/services /etc/services.d/
COPY docker/etc/nginx/nginx.conf /etc/nginx/nginx.conf
# Make service scripts executable
RUN chmod +x /etc/services.d/*/run /etc/services.d/*/finish
# Copy application files from builders
COPY --from=backend-builder /app/dist /app/backend/dist
COPY --from=backend-builder /app/node_modules /app/backend/node_modules
COPY --from=backend-builder /app/package.json /app/backend/package.json
COPY --from=backend-builder /app/drizzle.config.ts /app/backend/drizzle.config.ts
# Copy only the schema file for Drizzle migrations
COPY --from=backend-builder /app/src/db/schema.ts /app/backend/src/db/schema.ts
COPY --from=frontend-builder /app/.next /app/frontend/.next
COPY --from=frontend-builder /app/node_modules /app/frontend/node_modules
COPY --from=frontend-builder /app/package.json /app/frontend/package.json
COPY --from=frontend-builder /app/public /app/frontend/public
# Configure volumes and ports
VOLUME ["/app/backend/data", "/app/backend/uploads"]
EXPOSE 8080
# Set up healthcheck
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/ || exit 1
ENTRYPOINT ["/init"]

View File

@ -7,12 +7,17 @@ http {
absolute_redirect off;
server {
listen 80;
server_name localhost:8080;
listen 8080;
server_name localhost;
# Global timeout settings for all locations
proxy_read_timeout 86400s; # 24 hours
proxy_send_timeout 86400s; # 24 hours
proxy_connect_timeout 60s; # Connection establishment timeout
# API requests
location /api {
proxy_pass http://perplexica-backend:3001;
proxy_pass http://localhost:3001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
@ -21,7 +26,7 @@ http {
# WebSocket requests
location /ws {
proxy_pass http://perplexica-backend:3001;
proxy_pass http://localhost:3001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
@ -33,7 +38,7 @@ http {
# Frontend requests
location / {
proxy_pass http://perplexica-frontend:3000;
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

View File

@ -0,0 +1,3 @@
#!/usr/bin/with-contenv bash
s6-svc -d /var/run/s6/services/frontend
s6-svc -d /var/run/s6/services/nginx

View File

@ -0,0 +1,8 @@
#!/usr/bin/with-contenv bash
cd /app/backend
# Run database migrations before starting the app
yarn db:push
# Start the application
exec node dist/app.js

View File

@ -0,0 +1,2 @@
#!/usr/bin/with-contenv bash
s6-svc -d /var/run/s6/services/nginx

View File

@ -0,0 +1,3 @@
#!/usr/bin/with-contenv bash
cd /app/frontend
exec node_modules/.bin/next start

View File

@ -0,0 +1,2 @@
#!/usr/bin/with-contenv bash
exec nginx -g "daemon off;"

View File

@ -1,12 +1,10 @@
# Exposing Perplexica to a Network
# Accessing Perplexica over a Network
This guide explains how to make Perplexica available over a network using the built-in Nginx reverse proxy.
This guide explains how to access Perplexica over your network using the nginx reverse proxy included in the Docker setup.
## Accessing Perplexica Over a Network
## Basic Network Access
### Basic Access
With the Nginx reverse proxy, Perplexica is automatically accessible from any device on your network:
Perplexica is automatically accessible from any device on your network:
1. Start Perplexica using Docker Compose:
```bash
@ -23,28 +21,15 @@ With the Nginx reverse proxy, Perplexica is automatically accessible from any de
http://YOUR_SERVER_IP:8080
```
### Domain Configuration
If you have a domain name, you can point it to your server:
1. Configure your domain's DNS settings to point to your server IP
2. Access Perplexica via:
```
http://your-domain.com:8080
```
## Advanced Configuration
### Custom Port
## Custom Port Configuration
If you need to use a different port instead of the default 8080:
1. Modify the `docker-compose.yaml` file:
```yaml
nginx:
perplexica:
ports:
- "YOUR_CUSTOM_PORT:80"
- "YOUR_CUSTOM_PORT:8080"
```
2. Restart the containers:
@ -52,55 +37,10 @@ If you need to use a different port instead of the default 8080:
docker compose down && docker compose up -d
```
### SSL/HTTPS Configuration
For secure HTTPS access:
1. Modify the Nginx configuration to include SSL:
```nginx
# In nginx.conf
server {
listen 80;
listen 443 ssl;
ssl_certificate /path/to/certificate.crt;
ssl_certificate_key /path/to/private.key;
# Rest of configuration...
}
```
2. Update the Docker volume to include your certificates:
```yaml
nginx:
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/path/to/ssl:ro
```
3. Restart the containers:
```bash
docker compose down && docker compose up -d
```
4. Or just use another reverse proxy on top of this one...
## Troubleshooting
If you encounter issues accessing Perplexica over your network:
1. **Firewall Settings**: Ensure port 8080 (or your custom port) is allowed in your firewall
2. **Docker Network**: Check if Docker's network settings allow external connections:
```bash
docker network inspect perplexica_perplexica-network
```
3. **Nginx Logs**: Check for any connection issues:
```bash
docker logs perplexica-nginx-1
```
4. **Direct Access**: Verify if you can access the services directly:
- Frontend: http://YOUR_SERVER_IP:3000
- Backend: http://YOUR_SERVER_IP:3001
2. **Docker Logs**: Check for any connection issues with `docker logs perplexica`
3. **Network Access**: Make sure your devices are on the same network and can reach the server

View File

@ -41,39 +41,83 @@ type RecursivePartial<T> = {
[P in keyof T]?: RecursivePartial<T[P]>;
};
const loadConfig = () =>
toml.parse(
const loadConfig = () => {
try {
return toml.parse(
fs.readFileSync(path.join(__dirname, `../${configFileName}`), 'utf-8'),
) as any as Config;
} catch (error) {
// Return default config if file doesn't exist
return {
GENERAL: {
PORT: 3001,
SIMILARITY_MEASURE: 'cosine',
KEEP_ALIVE: '5m',
},
MODELS: {
OPENAI: {
API_KEY: '',
},
GROQ: {
API_KEY: '',
},
ANTHROPIC: {
API_KEY: '',
},
GEMINI: {
API_KEY: '',
},
OLLAMA: {
API_URL: '',
},
CUSTOM_OPENAI: {
API_URL: '',
API_KEY: '',
MODEL_NAME: '',
},
},
API_ENDPOINTS: {
SEARXNG: '',
},
};
}
};
export const getPort = () => loadConfig().GENERAL.PORT;
export const getPort = () =>
process.env.PORT ? parseInt(process.env.PORT, 10) : loadConfig().GENERAL.PORT;
export const getSimilarityMeasure = () =>
loadConfig().GENERAL.SIMILARITY_MEASURE;
process.env.SIMILARITY_MEASURE || loadConfig().GENERAL.SIMILARITY_MEASURE;
export const getKeepAlive = () => loadConfig().GENERAL.KEEP_ALIVE;
export const getKeepAlive = () =>
process.env.KEEP_ALIVE || loadConfig().GENERAL.KEEP_ALIVE;
export const getOpenaiApiKey = () => loadConfig().MODELS.OPENAI.API_KEY;
export const getOpenaiApiKey = () =>
process.env.OPENAI_API_KEY || loadConfig().MODELS.OPENAI.API_KEY;
export const getGroqApiKey = () => loadConfig().MODELS.GROQ.API_KEY;
export const getGroqApiKey = () =>
process.env.GROQ_API_KEY || loadConfig().MODELS.GROQ.API_KEY;
export const getAnthropicApiKey = () => loadConfig().MODELS.ANTHROPIC.API_KEY;
export const getAnthropicApiKey = () =>
process.env.ANTHROPIC_API_KEY || loadConfig().MODELS.ANTHROPIC.API_KEY;
export const getGeminiApiKey = () => loadConfig().MODELS.GEMINI.API_KEY;
export const getGeminiApiKey = () =>
process.env.GEMINI_API_KEY || loadConfig().MODELS.GEMINI.API_KEY;
export const getSearxngApiEndpoint = () =>
process.env.SEARXNG_API_URL || loadConfig().API_ENDPOINTS.SEARXNG;
export const getOllamaApiEndpoint = () => loadConfig().MODELS.OLLAMA.API_URL;
export const getOllamaApiEndpoint = () =>
process.env.OLLAMA_API_URL || loadConfig().MODELS.OLLAMA.API_URL;
export const getCustomOpenaiApiKey = () =>
loadConfig().MODELS.CUSTOM_OPENAI.API_KEY;
process.env.CUSTOM_OPENAI_API_KEY || loadConfig().MODELS.CUSTOM_OPENAI.API_KEY;
export const getCustomOpenaiApiUrl = () =>
loadConfig().MODELS.CUSTOM_OPENAI.API_URL;
process.env.CUSTOM_OPENAI_API_URL || loadConfig().MODELS.CUSTOM_OPENAI.API_URL;
export const getCustomOpenaiModelName = () =>
loadConfig().MODELS.CUSTOM_OPENAI.MODEL_NAME;
process.env.CUSTOM_OPENAI_MODEL_NAME || loadConfig().MODELS.CUSTOM_OPENAI.MODEL_NAME;
const mergeConfigs = (current: any, update: any): any => {
if (update === null || update === undefined) {