Self-hosting Feedbin with Docker and OpenShift

Feedbin is an excellent RSS feed reader. You can sign up for its service and support its development. The company is gracious enough to offer its source code for DIYers to play with.

Prerequisites

API keys for Stripe and Skylight are required for running Feedbin. In addition, you need to pick a password for the PostgreSQL database. Populate the information in these two files:

db.env:

POSTGRES_PASSWORD=ChangeMe!
POSTGRES_DB=feedbin
POSTGRES_USER=feedbin

secrets.env:

PUSH_URL=http://feedbin.yourdomain.tls
SECRET_KEY_BASE=48_bit_secret
SKYLIGHT_AUTHENTICATION=
STRIPE_API_KEY=
STRIPE_PUBLIC_KEY=

Running Feedbin with Docker

You can find two Docker images here and here. The first image has to be used together with postgres, redis, memcached, elasticsearch, and nginx. The second image is all-in-one, minus postgres.

For the all-in-one image, you just need docker-compose.yml below in the same directory as the two files above. Then, run docker-compose up -d, and your Feedbin instance can be found at http://localhost:3000

docker-compose.yml:

version: '3'

services:
  db:
    image: postgres:10.4
    restart: always
    env_file:
      - db.env
    volumes:
      - db:/var/lib/postgresql/data

  app:
    image: rocwhite/feedbin-aio
    restart: always
    environment:
      - POSTGRES_HOST=db
    env_file:
      - db.env
      - secrets.env
    volumes:
      - app:/srv/apps/feedbin/current/public
    depends_on:
      - db

volumes:
  db:
  es:
  app:

With the other image, use this docker-compose.yml instead, along with a nginx.conf file.

docker-compose.yml:

version: '3'

services:
  db:
    image: postgres:10.4
    restart: always
    env_file:
      - db.env
    volumes:
      - db:/var/lib/postgresql/data

  rds:
    image: redis:3.2
    restart: always

  mcd:
    image: memcached:1.5
    restart: always

  es:
    image: elasticsearch:2.4
    restart: always
    volumes:
      - es:/usr/share/elasticsearch/data

  app:
    image: rocwhite/feedbin
    restart: always
    environment:
      - POSTGRES_HOST=db
      - MEMCACHED_HOST=mcd
      - REDIS_HOST=rds
      - ELASTIC_SEARCH_HOST=es
    env_file:
      - db.env
      - secrets.env
    volumes:
      - app:/srv/apps/feedbin/current/public
    depends_on:
      - db
      - rds
      - mcd
      - es

  web:
    image: nginx
    restart: always
    volumes:
      - app:/srv/apps/feedbin/current/public:ro
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - app

volumes:
  db:
  es:
  app:

nginx.conf:

## Lines starting with two hashes (##) are comments with information.
## Lines starting with one hash (#) are configuration parameters that can be uncommented.
##
###################################
##         configuration         ##
###################################
##
## See installation.md#using-https for additional HTTPS configuration details.

upstream app {
  server app:3000;
}

## Normal HTTP host
server {
  listen 0.0.0.0:80 default_server;
  listen [::]:80 ipv6only=on default_server;
  server_tokens off; ## Don't show the nginx version number, a security best practice
  root /srv/apps/feedbin/current/public;

  ## Increase this if you want to upload large attachments
  client_max_body_size 20m;

  ## Individual nginx logs for this vhost
  access_log  /var/log/nginx/access.log;
  error_log   /var/log/nginx/error.log;

  location / {
    ## Serve static files from defined root folder.
    ## @app is a named location for the upstream fallback, see below.
    try_files $uri $uri/index.html $uri.html @app;
  }

  ## If a file, which is not found in the root folder is requested,
  ## then the proxy passes the request to the upsteam (unicorn).
  location @app {
    ## If you use HTTPS make sure you disable gzip compression
    ## to be safe against BREACH attack.
    # gzip off;

    proxy_read_timeout      300;
    proxy_connect_timeout   300;
    proxy_redirect          off;

    proxy_set_header    Host                $http_host;
    proxy_set_header    X-Real-IP           $remote_addr;
    proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
    proxy_set_header    X-Forwarded-Proto   $scheme;
    proxy_set_header    X-Frame-Options     SAMEORIGIN;

    proxy_pass http://app;
  }

  ## Enable gzip compression as per rails guide:
  ## http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression
  ## WARNING: If you are using relative urls remove the block below
  ## See config/application.rb under "Relative url support" for the list of
  ## other files that need to be changed for relative url support
  location /assets/ {
    gzip_static on; # to serve pre-gzipped version
    expires max;
    add_header Cache-Control public;
  }

  error_page 502 /502.html;
}

Running Feedbin on OpenShift

You probably need a Pro instance with > 1GB memory to run Feedbin. Once you create a new project, import db.env and secrets.env (see above) as secrets:

oc create secret generic feedbin-db --from-env-file=./db.env --type=opaque
oc create secret generic feedbin-secrets --from-env-file=./secrets.env --type=opaque

Then create a new app using the template file below.

oc new-app -f ./openshift-aio.yml

openshift-aio.yml

apiVersion: v1
kind: Template
metadata:
  name: feedbin
objects:
- apiVersion: v1
  kind: PersistentVolumeClaim
  metadata:
    name: data
  spec:
    accessModes:
    - ReadWriteOnce
    resources:
      requests:
        storage: 1Gi
- apiVersion: v1
  kind: ImageStream
  metadata:
    labels:
      app: feedbin
    name: feedbin
  spec:
    lookupPolicy:
      local: false
    tags:
    - from:
        kind: DockerImage
        name: rocwhite/feedbin-aio
      importPolicy: {}
      name: latest
      referencePolicy:
        type: Source
- apiVersion: v1
  kind: DeploymentConfig
  metadata:
    labels:
      name: feedbin
    name: feedbin
  spec:
    replicas: 1
    selector:
      deploymentconfig: feedbin
    strategy:
      activeDeadlineSeconds: 21600
      recreateParams:
        timeoutSeconds: 600
      resources: {}
      type: Recreate
    template:
      metadata:
        name: feedbin
        labels:
          name: feedbin
          deploymentconfig: feedbin
      spec:
        containers:
          - name: db
            image: registry.access.redhat.com/rhscl/postgresql-10-rhel7
            env:
              - name: POSTGRESQL_DATABASE
                valueFrom:
                  secretKeyRef:
                    key: POSTGRES_DB
                    name: feedbin-db
              - name: POSTGRESQL_PASSWORD
                valueFrom:
                  secretKeyRef:
                    key: POSTGRES_PASSWORD
                    name: feedbin-db
              - name: POSTGRESQL_USER
                valueFrom:
                  secretKeyRef:
                    key: POSTGRES_USER
                    name: feedbin-db
            volumeMounts:
              - name: feedbin-data
                mountPath: /var/lib/pgsql/data
                subPath: psql-data
            resources:
              limits:
                cpu: 150m
                memory: 100Mi
          - name: feedbin
            image: rocwhite/feedbin-aio
            imagePullPolicy: Always
            env:
              - name: NUM_UNICORN_WORKER
                value: "2"
              - name: discovery.type
                value: "single-node"
              - name: PUSH_URL
                value: 'http://feedbin.yourdomain.tls
              - name: SECRET_KEY_BASE
                valueFrom:
                  secretKeyRef:
                    key: SECRET_KEY_BASE
                    name: feedbin-secrets
              - name: SKYLIGHT_AUTHENTICATION
                valueFrom:
                  secretKeyRef:
                    key: SKYLIGHT_AUTHENTICATION
                    name: feedbin-secrets
              - name: STRIPE_API_KEY
                valueFrom:
                  secretKeyRef:
                    key: STRIPE_API_KEY
                    name: feedbin-secrets
              - name: STRIPE_PUBLIC_KEY
                valueFrom:
                  secretKeyRef:
                    key: STRIPE_PUBLIC_KEY
                    name: feedbin-secrets
              - name: POSTGRES_DB
                valueFrom:
                  secretKeyRef:
                    key: POSTGRES_DB
                    name: feedbin-db
              - name: POSTGRES_PASSWORD
                valueFrom:
                  secretKeyRef:
                    key: POSTGRES_PASSWORD
                    name: feedbin-db
              - name: POSTGRES_USER
                valueFrom:
                  secretKeyRef:
                    key: POSTGRES_USER
                    name: feedbin-db
            resources:
              limits:
                cpu: 1350m
                memory: 770Mi
            volumeMounts:
              - name: feedbin-data
                mountPath: /usr/share/elasticsearch/data
                subPath: es-data
        volumes:
          - name: feedbin-data
            persistentVolumeClaim:
              claimName: data
        restartPolicy: Always
    test: false
    triggers:
    - type: ConfigChange
    - imageChangeParams:
        automatic: true
        containerNames:
        - feedbin
        from:
          kind: ImageStreamTag
          name: feedbin:latest
          # namespace:
      type: ImageChange
- apiVersion: v1
  kind: Service
  metadata:
    name: feedbin
  spec:
    ports:
    - name: web
      port: 8080
      protocol: TCP
      targetPort: 3000
    selector:
      name: feedbin
    sessionAffinity: None
    type: ClusterIP
- apiVersion: v1
  kind: Route
  metadata:
    name: feedbin
  spec:
    host:
    port:
      targetPort: web
    tls:
      insecureEdgeTerminationPolicy: Redirect
      termination: edge
    to:
      kind: Service
      name: feedbin
      weight: 100
    wildcardPolicy: None

Comments

Comments powered by Disqus