# Build stage - Download release binaries instead of building from source
FROM alpine:3.22 AS downloader
# Arguments for the release version and target platform
ARG VERSION=v0.1.0-alpha.1
ARG TARGETPLATFORM
ARG TARGETOS=linux
ARG TARGETARCH
# FalkorDB Cypher skills version. Local builds default to main; release builds pass an immutable commit SHA.
ARG SKILLS_REF=main
# Install download dependencies
RUN apk add --no-cache wget tar
# Download FalkorDB Cypher skills
RUN set -eu; \
if [ -z "${SKILLS_REF:-}" ]; then \
echo "SKILLS_REF build arg must not be empty. Use a pinned commit SHA for release builds; use branch refs only for local/dev builds."; \
exit 1; \
fi; \
echo "Downloading FalkorDB Cypher skills (ref: ${SKILLS_REF})" && \
wget -O /tmp/skills.tar.gz "https://github.com/FalkorDB/skills/archive/${SKILLS_REF}.tar.gz" && \
mkdir -p /tmp/skills-extract && \
tar -xzf /tmp/skills.tar.gz -C /tmp/skills-extract --strip-components=1 && \
mv /tmp/skills-extract/cypher-skills /tmp/cypher-skills && \
rm -rf /tmp/skills.tar.gz /tmp/skills-extract && \
echo "✅ Downloaded $(ls /tmp/cypher-skills | wc -l) skills"
# Download the appropriate binary based on target architecture with retry logic
RUN echo "Downloading text-to-cypher ${VERSION} for ${TARGETPLATFORM} (${TARGETOS}/${TARGETARCH})" && \
case "${TARGETARCH}" in \
"amd64") export RUST_ARCH="x86_64-musl" ;; \
"arm64") export RUST_ARCH="aarch64-musl" ;; \
*) echo "Unsupported architecture: ${TARGETARCH}" && exit 1 ;; \
esac && \
DOWNLOAD_URL="https://github.com/FalkorDB/text-to-cypher/releases/download/${VERSION}/text-to-cypher-linux-${RUST_ARCH}.tar.gz" && \
echo "Download URL: ${DOWNLOAD_URL}" && \
for i in 1 2 3 4 5; do \
echo "Download attempt $i/5..." && \
if wget -O /tmp/text-to-cypher.tar.gz "${DOWNLOAD_URL}"; then \
echo "✅ Download successful on attempt $i" && \
break; \
else \
echo "❌ Download attempt $i failed" && \
if [ $i -eq 5 ]; then \
echo "All download attempts failed" && \
exit 1; \
fi && \
echo "Waiting 10 seconds before retry..." && \
sleep 10; \
fi; \
done && \
cd /tmp && \
tar -xzf text-to-cypher.tar.gz && \
chmod +x text-to-cypher
# Verify the binary works
RUN test -x /tmp/text-to-cypher && echo "Binary verification completed"
# Runtime stage - Use FalkorDB as base image
FROM falkordb/falkordb:latest
# Repair the minimal base image package state before installing supervisord.
RUN apt-get update && \
apt-get -y --fix-broken install && \
apt-get install -y bash perl-base passwd ca-certificates supervisor && \
rm -rf /var/lib/apt/lists/*
# Create a non-root user for the managed services.
RUN groupadd -g 1000 appuser && \
useradd -m -s /bin/bash -u 1000 -g appuser appuser
# Set the working directory for our application
WORKDIR /app
# Copy the compiled binary from the downloader stage
COPY --from=downloader /tmp/text-to-cypher /app/text-to-cypher
# Copy the templates from the downloaded package (contains the correct templates for this version)
COPY --from=downloader /tmp/templates ./templates
# Copy FalkorDB Cypher skills for dynamic skill loading
COPY --from=downloader /tmp/cypher-skills /app/skills
# Enable dynamic Cypher skills by default
ENV SKILLS_DIR=/app/skills
# Create import directory and set permissions
RUN mkdir -p /var/lib/FalkorDB/import && \
mkdir -p /tmp/falkordb-import && \
chown -R appuser:appuser /var/lib/FalkorDB/import && \
chmod -R 755 /var/lib/FalkorDB/import && \
chown -R appuser:appuser /tmp/falkordb-import && \
chmod -R 755 /tmp/falkordb-import && \
chown -R appuser:appuser /var/lib/falkordb && \
chmod -R 755 /var/lib/falkordb && \
chown -R appuser:appuser /var/lib/FalkorDB && \
chmod -R 755 /var/lib/FalkorDB
# Change ownership to the non-root user
RUN chown -R appuser:appuser /app
# Expose the ports your application runs on (in addition to FalkorDB's ports)
EXPOSE 8080 3001
# Copy supervisord configuration and scripts
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY entrypoint.sh /entrypoint.sh
# Create supervisor log directory and make scripts executable
RUN mkdir -p /var/log/supervisor && \
chmod +x /entrypoint.sh
# Use ENTRYPOINT instead of CMD to ensure it runs
ENTRYPOINT ["/entrypoint.sh"]