This the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Elekto Documentation

Elekto is an open-source software for securely conducting election. Elekto was inspired by the long-running CIVS project and built to conduct kubernetes steering and community elections. The project is hosted and maintained by CNCF.

If you have reached here trying to vote in an online election, you are in the wrong place.

Elekto was designed to support the following:

  • 100% GitOps workflow for configuration and election administration
  • 100% Oauth-driven workflow for voters (no emails)
  • Preference election voting (starting with Condorcet)
  • Multiple elections for the same organization
  • Responsive web design
  • Secret ballot voting

This set of documentation covers Elekto version 0.6.X.

1 - Overview

Overview of Elekto Project.

The Elekto Project is a CNCF accessory project which develops online voting software intended for use by the CNCF itself and by open source cloud native projects.

Originally written for the needs of the Kubernetes and OpenTelemetry projects, the Elekto software is designed to support online preference voting for open source projects. Based on a GitOps workflow and Condorcet election accounting, Elekto supports your steering committee and TOC elections, committee decision making, and anywhere else a formal project vote is required among a small pool of candidates or options.

Read on to learn more about Elekto’s goals and architecture.

1.1 - Architecture

Understand the working of elekto.

The application requires a git repository to store election meta files. The meta repository is the single source of truth for the application and is managed by gitops, all the tasks like creating an election, adding/removing voters to the list are managed by raising specific pull requests in the meta repository.


1.2 - Design Goals

Reasons behind the Elekto design

Elekto was originally built to support the Kubernetes Steering Committee elections, supporting the existing workflow of that community. It’s also intended to avoid several chronic issues with usage of the CIVS election system.

Before adopting, or contributing to Elekto, please read these principles as they give you a broad idea of what kinds of features and changes the project is interested in or likely to implement.

Preference Elections for Small Organizations

Elekto is designed only to support “preference elections” where voters rank candidates. We have found preference elections to offer better outcomes than other methods, such as plurality.

Currently, only the Condorcet preference election algorithm is supported, but contributions of other preference election accounting methods would be very welcome.

Elekto is not designed to support public governmental elections, or any election that might attract either thousands of voters, or a determined effort to compromise the system.


The project is designed to be an extremely simple, lightweight web application. Simplicity makes it easy to install and support in a variety of environments and platforms. This means that organizations can run Elekto as a microservice, on their own, instead of requiring a paid, hosted service.

Simplicity also makes Elekto easy to fork and modify, as well as contribute to. We chose Flask as our framework with this in mind. It’s also why our container image is a simple, unified image.

This does mean that changes that would substantially increase the complexity of Elekto – such as a theme engine or porting it to Django or decomposing it into a half-dozen scalable subservices – are unlikely to be accepted in the main project.

No Email

Elekto does not send any email, or other types of messages, to anyone. 100% of Elekto interaction is via GitOps and the Web UI.

This requirement is based on experience with other election systems. Email today is a singularly unreliable way to deliver ballots or other election messages, and other channels aren’t much better. Anyone who has administered an election under CIVS can tell you that 80% of their work is troubleshooting email delivery issues.


Elekto is intended to be run as a microservice, with one Elekto instance per organization or team. It is not intended to run as a multi-tenant service; instead, run a separate Elekto instance for each user.

Each Elekto instance connects to only one metadata repository, one database, and one Oauth source. Again, if you need multiple configurations, the solution is to run multiple Elekto instances.

Elekto supports any number of elections for that one organization.

GitOps Administration

The practice of defining adminstrator actions and application configuration via changes to files in a Git repository is called “GitOps”. Elekto administration is based entirely on GitOps, which offers the following advantages:

  • Eliminates multiple webforms, keeping the codebase smaller
  • Allows organizations to define their change and approval workflow through their git repo, including owner roles and multi-stage approval and revision
  • Provides a transparent, long-term historical record of any and all election setup changes
  • Unifies documentation and configuration
  • Reduces dependency on database hosting

As such, the only data in Elekto that does not live in a git repo is data that needs to be private/secret: ballots, raw election results, and exception requests.


Elekto is aimed at public open source projects, which means that complete transparency of all election steps is required and provided. As such, the metadata git repo and the Elekto instance are expected to be publicly readable, and no provisions have been made for running Elekto using private repos or webservers on closed networks.

Transparency is also carried out in other design decisions. For example, while only designated voters can vote in an election, any user (or any authenticated visitor) can view the details of any election.

Secret Ballot

Voters must be able to vote without their individual ballots being easily snoopable, whether by other voters, election officers, or infrastructure staff. In a small organization, infra maintainers and officers are usually voters and sometimes candidates themselves, and as such ballot identity needs to be hidden even from someone with direct access to the database.

Elekto was also built with the secondary requirement of allowing voters to re-cast their ballots up until the election deadline. However, individual ballot auditablilty was not a requirement, so it is not yet implemented (contributions welcome). Aggregate ballot auditablity is incompatible with secrecy, and as such is out of scope.

2 - Voting

A guide for users: how to vote using Elekto.

Voting With Elekto

What follows is a User Guide for regular voters using Elekto to vote. This guide assumes that you have been directed to a particular Elekto site running an election for your organization.

Voting with Elekto is designed to be easy and as intuitive as a preference election can be.

Log In

Elekto uses Oauth authentication against an external provider chosen by your administrator. In most cases, the provider is GitHub, and these docs will assume that’s the case.

When you access Elekto, you are presented with a login screen that requests you to “Sign In With GitHub”. Click on that button.

Elekto login screen

The first time you log in to a particular Elekto site, you will be asked by your provider (GitHub) to verify your identity. Generally that means logging into the provider and authorizing Elekto to verify your identity there. You need to authorize, or you will not be able to use Elekto. After that first time, simply clicking the button is sufficient to log you in.

Find The Election

Elekto navigation is on the left on a desktop interface. On mobile, there is a pull-down menu.

To access current and future elections, select “Explore Election” on the left. Historical elections (that is, ones where all voting is closed) can be found under “Past Elections”.

View of navigation menu

If you select Explore Election, you’ll be presented with a list of one or more upcoming or currently voting elections in your organization. These elections are not filtered by your voter status, so you will be able to see elections where you cannot vote. The list shows election names, organization or team (in case your community has multiple teams with different elections), status, and the dates that the election is open for voting. Statuses are:

  • Upcoming: voting in this election has not started yet
  • Current: voting is open
  • Completed: voting is closed

All dates and times are UTC.

elections listing, showing two elections

Find the election you are interested in in the list, and click its name.

Viewing an Election

The Election screen packs a lot of information into a compact screen. See the illustration below which explains what all the parts of the interface are:

Election information screen with labels showing the parts of the interface

Here’s the important parts of the interface:

  • Title of the Election
  • Organization, in case your community has multiple teams of people voting on things
  • Election Status (see above)
  • Voter Status: “Eligible” if you can vote in this election, “Not Eligible” if you can’t
  • Election description, written by your election administrators
  • List of Candidates (see below)
  • Voting dates in the election
  • Vote button: only appears when the election has started, and if you are eligible

There are two actions you can take before an election starts: browsing candidate profiles, and requesting a voter qualification exception.

View Candidate Profiles

Below the election description there is a list of candidates in random order. Click on any of these candidates to see the profile they submitted to the election. This profile opens in a new tab in most browsers, in order to prevent you from resetting an incomplete ballot if you click on a profile in the middle of voting.

picture of individual candidate profile

The Candidate profile consists of at least the candidate’s name, their GitHub handle, and a self-written description of the candidate and their qualifications. Depending on your organization’s requirements, it may contain other pieces of required information, such as their employer, Slack handle, or years of membership. These profiles are submitted as PRs through the election repository, so if you don’t see a profile for a candidate, that usually means that they haven’t submitted one yet.

Close the tab to exit the candidate profile.

Asking for Exceptions

The election screen displays if you are ineligible to vote:

example election with not eligible

Many organizations have a process whereby a member of their community who does not initially qualify for voter eligibility can request an exception to the requirements in order to vote. You can do this through Elekto.

exception request form

If the election information page shows “Not Eligible”, then there will also be a link you can click to request an exception. This form will ask for some contact information for you (so that the election officers can reach you) and has a large text box for you to explain why you are qualified for an exception. What information you need to put there depends on your organization.

There is also a deadline for exception requests, which may be before the deadline for voting. If you attempt to request an exception after that date, the form will not be available.


Once the election has started, you can cast your vote for your preferred candidates. From the election information screen, click the button that says “Vote”.

voting interface

Elekto manages preference elections, where you choose candidates in order of which one you’d prefer most. This means that, rather than just checking off a candidate, you need to rank all candidates in order of your preference. While decisions about who you would prefer to whom are up to you, the Elekto UI offers you two ways to save that ranking and cast your ballot.

  • Click-and-drag: you can drag candidate names up and down to resort them. Elekto will do its best to rank them based on where you’ve placed them, but can sometimes get numerically confused. If that’s an issue, use the second method.
  • Numerical Ranking: next to each candidate is a numbered drop-down. You can select a number ranking for each candidate, with “1” being your most preferred candidate, and higher numbers being less preferred.

You are allowed to “tie” two candidates, giving them the same number ranking, if you cannot decide between them. Depending on your election officers' settings, you may also use “No Opinion” to indicate candidates that you know nothing about and thus cannot rank them. Otherwise, please do try to rank all candidates, as this allows Elekto to select a most preferred candidate without a deadlock.

When you have finished ranking candidates, you may optionally enter a passphrase that will allow you to re-cast your vote. This passphrase is used to encrypt the link between your account and your vote, so that the Election Officers are unable to eavesdrop on your specific ballot. If you enter a passphrase, you need to remember it or write it down because it is not retrievable via Elekto. If you know that there is no chance you will want to re-do your ballot before the election is over, then you can leave the passphrase empty.

Reviewing and Re-Casting Your Vote

If permitted by your administrator, Elekto allows you to review, revoke and re-cast your vote for as long as the election is open. This way, you can vote when it opens, and update your vote if you learn new information about candidates. If not permitted, you will not see the View option.

In order to ensure that this doesn’t compromise the privacy of your ballot, though, the link between your identity and ballot are encrypted with a passphrase you supplied when you voted (see above). As such, you must enter that passphrase in the field in the election UI in order to review your vote. If you didn’t set one, or cannot remember it, you cannot re-cast your vote. Administrators will not be able to help.

If you enter your passphrase, and click “View”, and you will be presented with a read-only view of your ballot. You can then check if the ballot still matches how you wish to vote.

If it does not, you can revoke your ballot from the View screen. Simply enter your passphrase again, and then press “Revoke”. Your original ballot will be deleted. You can then select “vote” in order to cast your ballot again. Note that, if you revoke your ballot and do not vote again, you are canceling your vote in that election.

Checking the Results

As soon as the election ends, the Election Officers will be sent the results of the election. However, since organizations have complex rules, including corporate representation limits, requirements for diversity, and limits on disclosure of absolute rankings, you will not be able to see the results as a voter until a hand-edited version of them is published by an Election Officer.

election results page

Once it is published, the results will be available via a link from the election information page.

3 - Administration

Guide for running elections using Elekto.

Administering elections in Elekto is done through pushes of structured files to a GitHub repository. See the configuration guide for how to configure the link to the repository. This guide outlines how the Election Administrators run an individual election. There are three parts to this:

  1. Creating an election
  2. Running an election
  3. Concluding an election

GitOps and Administration

All Administrative actions happen through a single GitHub repository, or via the Elekto web application. As such, any references to files or directories in this Guide are references to files and directories under the chosen repository.

For that matter, “administrator actions” in general usually refers to merging files or changes into the designated git repository and branch. Only two actions take place in the Elekto UI: reviewing voter exceptions and calculating election results. Everything else happens because an admin changed something in a file and that change was merged.

This means that it is up to your organization to enforce correct permissions and process for the repository and its directories so that administrators can merge what they’re supposed to be able to merge (and not what they’re not, like someone else’s election). Ideally, your project’s CICD workflow will support this.

3.1 - Concluding an Election

Guide for finishing an election in Elekto.

Completing the Election

Once the end date for the election is past, it is time for the Election Officers to conclude the election. This has two steps: computing the candidate rankings, and publishing the election results.

Computing the Rankings

After the end_datetime, Election Officers will be presented with a button on the Administrator Console to compute the election rankings.

Administrator console with compute winners button

Clicking that button does two things. First, it calculates the preference election mathematical results according to the configured election method, displays those results, and saves them to the database. Second, if delete_after is set, it deletes all of the encrypted links between voters and ballots as an additional privacy measure.

Administrator console with election results

Once the election is calculated, it may not be “re-opened” by extending the end date.

Publishing the Results

The candidate rankings computed by Elekto are absolute mathematical rankings, and do not take into account organizational rules such as employer, role, or diversity quotas in the elected body. Some organizations also require the election to be certified by another committee. As such, the raw election results are visible only to the Election Officers.

To share the election results with all voters, create a file. This is a simple, markdown-formatted file with no special headers. You add text in it to share the election results according to the rules of your organization.

example election results screen

What about Tie Votes?

In a preference election, two candidates with an identical ranking result in a tie. While Elekto uses special algorithms to prevent tie votes (such as, but default, Schultze), certain combinations of ballots result in an unbreakable tie, particularly in elections with low numbers of ballots.

In the case of a tie, it is up to your election rules to decide how to handle it. Usually, this means a runoff election.

3.2 - Creating an Election

Guide for creating a new election in Elekto.

The Election Directory

Create a new election in Elekto by creating a directory under the primary elections directory defined in the ELECTION_DIR configuration variable. What makes it an election directory or not is the presence of an election.yaml file; directories without this file will be ignored. This directory may be nested (e.g. elections/2021/TOC), but may not be nested under another election directory (one that has an election.yaml in it).

Anyone with appropriate permissions on the repository may create an election directory. Usually, creating the directory and file are the first actions of the selected Election Administrators.

Generally, after creating the directory, administrators create an election.yaml,, and voters.yaml file.

Election Administrators

Administrators are Elekto users who have special permissions to manage an individual election. Each election must have at least one, and can have any number although practically few elections require more than three. Administrators need not have any control of the underlying infrastructure, nor do they need rights on other elections. They need not even be qualified voters.

How election Administrators are selected is up to the organization running the election and its management of repository change permission. As far as Elekto is concerned, the people listed in election.yaml are the Administrators regardless of how they got there. Like Voters, Administrators log into Elekto with their OAuth IDs.

The election.yaml File

This YAML file is the main file that creates and configures an election. It contains multiple configuration variables, most of which can be changed at multiple times during the election. This file is required for the election to be recognized by Elekto, and if it has errors the election will not appear in the Elekto UI.

See the sample elections in elekto.meta.test for examples of these files.

election.yaml variables

name: Display name of the election. Required, changeable.

organization: Group for which this election is being run, for display purposes. Required, changeable.

start_datetime: datetime that voting for the election starts, in UTC time. At this datetime, the Vote button will be enabled automatically. Required. Changeable, but changing it after the election has already started can cause votes to be recorded or discarded improperly.

end_datetime: datetime that the election ends, in UTC time. At this time, the Vote button will be disabled automatically. Required. Changeable until the election ends and a winner has been calculated, after which changes to this field will have no effect.

no_winners: number of potential winners in the election, for display to the administrators. Required, changeable.

allow_no_opinion: True/False. Can voters choose not to rank some candidates? Required, changeable until the election begins, after which changes to this field may cause votes to be counted incorrectly.

delete_after: True/False, whether to delete the encrypted links between voters and ballots once election results are calculated. Extra voter privacy measure. Required, changeable until results are calculated.

show_candidate_fields: List. A list of additional display fields for the candidate information pages. Labels must match the candidate profile fields exactly, or the data will not be displayed. Optional, changeable at any time.

election_officers: List of Oauth IDs of the election officers, determining who they are. Required, changeable at any time. Election officers need not be voters.

eligibility: Text to display in the “eligibility” section of the UI, for voter information. Optional, changeable at any time.

exception_description: Text to display before the Request Exception link. Optional, changeable at any time.

exceptions_due: Datetime (in UTC) when exception requests will no longer be accepted, and the Exceptions button will be disabled. Can be any date before the end of the election. Required, changeable. If your election does not allow voter exceptions, then set this to the date you create the election.

The File

This file contains a Markdown-formatted description of the election for voter information purposes. Since this information is displayed directly above the list of candidates and the voting controls, it’s generally a good idea to limit it to about 1/3 page of text. If you have more election details than that, consider summarizing and linking off to a page on your own website.

This description should include, at a minimum:

  • A description of what’s being decided in the election
  • Who is hosting the election
  • Contact information for questions

Ideally, it will also include:

  • links to voter and candidate eligibility requirements
  • link to candidate nomination process
  • a short version of the election timeline

The voters.yaml File

The voters file contains a list of Oauth IDs of the qualified voters for the election, as a simple yaml list. If you wish to create an election before you have pulled the list of voters, then create an empty list until you add the voters.

Voters may be added at any time, until the end of the election. Voters may also be removed at any time, but if a voter is removed from the file after they have already voted, their ballot will not be deleted. As such, if voters need to be removed after an election has begun, it’s generally a good idea to abort the election and start a replacement one.

The contents of this file determine whether users see themselves as eligible to vote or not when they log into the UI. Voters are not otherwise notified when their status changes.


3.3 - Running an Election

Guide for running an election in Elekto.

An Elekto election includes a pre-election phase and an voting phase. Depending on your actual election process, the pre-election phase may be unnecessary or handled entirely outside Elekto. If so, ignore the pre-election phase instructions below.

A Note on Notifications

Elekto does not send notifications via email or other messaging to anyone. As such, it is up to your election administrators or officers to send out notifications using your normal community channels. All community members can check their current status and the status of the election at any time using the web UI, but they will not receive messages or reminders.

As such, when this Guide talks about sending reminders or notices, it is talking about action by the Election Officers or other community members.

Pre-election Phase

Two things happen between the time the election is announced and the time that voting starts. One is adding voters and acting on exceptions, and the second is adding nominated candidates.

The pre-election phase begins when the election is created in Elekto, and ends when voting starts.

Managing Voters

Voters are added to an election by populating the voters.yaml file. Obtaining a list of voters is up to you and your organization, as different groups have vastly different eligibility processes. Many CNCF projects obtain a list of voters by filing a ticket requesting a data pull from DevStats, CNCF’s contributor metrics system.

Once you have obtained a list of qualified voters and added them to voters.yaml, you should use your organization’s communications channels to announce that voters can now verify their status. Voters will be able to log into Elekto and check whether they are Eligible or not via the UI. If they find they are ineligible, they can request an exception (see below).

Until the election starts, you can freely add and remove voters from voters.yaml as your election rules dictate. We recommend that voters be sorted alphanumerically to make editing the list of voters easier. Elekto will ignore YAML comments, sort order, and even duplicates in the file.

Voter Exceptions

Organizations may allow people to challenge their voter ineligibility. Sometimes this is because the voter list process is error-prone, or it can be because some voter criteria are difficult to measure empirically. Based on the Kubernetes project, the term for requesting a ballot when the original voter list did not allow an individual to vote is a “voter exception”.

If your voter exception requests are public, then they can be handled via pull requests against the voter list. However, in Kubernetes and other projects, the exception requests are confidential, in order to spare members from embarrassment if their request is declined. For this reason, Elekto provides a mechanism for potential voters to request an exception through the UI.

The list of exceptions is available via the Admin screen in the UI. Access it by clicking the Admin button in the Election Information screen, which should be visible if you are an election officer and logged in.

photo of admin screen with several exception requests

From here you can see the list of requests. To keep track of which ones you have taken action on, you can click the button to indicate that the requests have been reviewed. This UI does not add voters whose exception requests are granted, however. To make voters eligible, you will need to modify the voters.yaml file in the repository. The requests screen is for tracking only.

Adding Nominated Candidates

Candidates get added to the Elekto system when they have completed and merged profiles. All profiles will need to be merged before the election begins. How candidates are nominated and qualified and their profiles are reviewed is entirely up to your organization; Elekto cares only that the file has the correct formatting and naming.

To support this, it’s usually helpful for the Election Officers to create a file as a template for potential candidates to copy. EOs are are usually also in charge of helping candidates with completing their profiles and sending out reminders to the community about nomination deadlines.

For more about candidate profiles, see the Candidate Guide.

Voting Phase

At the date and time indicated in the start_datetime setting, voters will be able to start voting. No further administrative action is required. During the voting phase, administrators should have little to do other than adding people to the voters list from the Exceptions.

The Administrator Console

Changing the Voters List While Voting

Most organizations continue to accept Voter Exception requests during the voting phase, so that voters can be added and cast ballots even if they are slow to check their status. Closing exceptions about three days before voting ends can be a good timeline.

There is no problem adding voters at any time before the end_datetime of the election. Once additional voters are merged into voters.yaml, they are immediately able to vote.

Removing voters from the list, however, is a bad idea once voting has started. Once a voter has cast a ballot, there is no way to delete their ballot. While administrators can check whether a particular voter has voted, deleting a voter can be subject to a race condition. That said, problems with duplicate voter IDs (e.g. one voter with two Github accounts) can be cleared up by deleting accounts during voting if you have the active cooperation of the voter.

Modifying the Election While Voting

Administrators can make changes to election.yaml and other settings during the election. Not all such changes are effective or wise, however. What follows are some notes on settings it makes sense to change during the voting phase. Settings that are not mentioned should not be changed. Elekto provides no guardrails here, though; administrators are technically able to change anything they wish.

  •, eligibility, exception_description: descriptive fields can be changed any number of times without worry.
  • end_datetime: you can change the end time of the election in order to give more time for voting as long as the election has not yet been concluded.
  • exceptions_due: you can also extend the Exceptions deadline until the election concludes.
  • no_winners, discard_after: these fields affect election conclusion, so they can be changed until then.
  • election_officers: officers can be added and removed from the election during voting.

You may also make changes and updates to candidate profiles during voting. However, adding or removing a candidate during voting would make it impossible to correctly compute the ballots. As such, if you need to change the list of candidates after voting starts, you will need to halt the election and hold a new one.

Retrieving and Canceling Ballots

During voting, voters may cancel and recast their ballot. Adminstrators, however, have no mechanism to cancel any voter ballot, or assist voters who have forgotten their passphrase. This is intentional, in order to prevent Election Officers from manipulating the election.

4 - Candidate Guide

Guide for candidates participating in an Elekto election

Your Candidate Profile

Your organization will have some type of nomination and confirmation process for how you become a candidate for election. From Elekto’s perspective, though, it’s much simpler. You are a candidate if you have a candidate profile file. The rest of this guide is therefore details on what needs to be in that file.

Creating a Candidate File

Usually your Election Officers will create a template file for you to copy and create your file. Your first step is to create a copy of this file with the following naming scheme:

The filename must start with candidate- to be recognized as a candidate file. The second half of the filename should match the ID field inside the candidate file, including case. If the names do not match, then the file will not be recognized as a candidate.

Contents of the Candidate File

Each candidate file is a markdown file with a small YAML header. Here’s a sample candidate file:

name: elekto
ID: elekto
- Language: Esperanto

## Reason for Name

"elekto" is "election" (or selection, or appointment) in the academic language Esperanto.  Also, it sounds like the name of a C-list superhero.

## Availability Search

Nobody currently seems to have trademarks on the name in the USA.  Various domains are available, as is the Github namespace.

The YAML header contains several fields to be filled in by the candidate:

  • name: the candidate’s name to be displayed in the UI
  • ID: the unique candidate ID. Usually this is their OauthID, but it can be any arbitrary string as long as it is unique for that election. It also needs to match the second half of the filename.
  • info: a list of key-value pairs that should match the show_candidate_fields

5 - Installation and Setup

Start using elekto for your organisation.

Elekto is a DIY service, so in order to start using it, you’re going to have to install it:

And then regardless of how you installed it:

5.1 - Development Installation

Create a dev install on your laptop

This guide will help you create a development installation of Elekto on your laptop, for hacking and testing purposes.

Create a development environment

The application is written in Python using Flask and SQLAlchemy. This repository ships a requirements.txt and an environment.yml for conda users.

# Installation with pip 
pip install -r requirements.txt

# Installation with Conda
conda env create -f environment.yml && conda activate elekto

Setup env variables

The repository has a .env.example file which can be used as a template for .env file, update the environment file after copying from .env.example.

# create a new .env file from .env.example
cp .env.example .env

Set the basic information about the application in the upper section

APP_NAME=k8s.elections     # set the name of the application
APP_ENV=development        # development | production   
APP_KEY=                   # random secret key (!! important !!)
APP_DEBUG=True             # True | False (production)
APP_URL=http://localhost   # Url where the application is hosted
APP_PORT=5000              # Default Running port for development 
APP_HOST=localhost         # Default Host for developmemt 

Update the database credentials,

DB_CONNECTION=mysql        # Mysql is only supported 

Update the meta repository info

META_SECRET=db5a951969c379e75d0bf15ad6ff8b4a36fbeb02  # same as webhook of the same meta repository

Update the Oauth info, create a GitHub Oauth app if already not created.


Migrate and Sync DB with Meta

The console script in the repository is used to perform all the table creations and syncing of the meta files.

# to migrate the database from the command line 
python console --migrate 

To sync the database with the meta files

# to the sync the database with the meta
python console --sync

Run the application Server locally

The flask server will start on 5000 by default but can be changed using --port option.

# to run the server on default configs
python console --run

# to change host and port
python console --port 8080 --host --run

5.2 - Installation

Full server installation documentation

This guide walks you through installing Elekto by hand on a server or VM.

Installation Concepts

For Elekto to run in production, you need the following small application stack:

  • The Elekto python/flask/uWSGI application
  • A backing SQL database instance
  • A web proxy such as Nginx or Kubernetes Ingress
  • A GitHub repository
  • An Oauth authentication source

Elekto can be installed either as a native application or as a container. The instructions below cover installation as a native application. For installation in a container, see Kubernetes installation

Installing Requirements and Python Binaries

Elekto is a Python/Flask/uWSGI application developed in Python 3. Building it from source requires the following prerequisites, which should be installed using your OS’s packaging system:

  • Python3 Pip and supporting build tools (like gcc), or Conda
  • uWSGI server
  • Database client libraries for your chosen database (see below)
  • git

Having installed those prerequisites, you can now install the python requirements using Pip, which will build the python source:

# Installation with pip 
pip install -r requirements.txt

# Installation with Conda
conda env create -f environment.yml && conda activate elekto

Please check the above build process carefully for error messages; the only acceptable errors are (a) warnings about out-of-date versions and (b) missing database libraries for the databases you’re not using.

It is possible that you could run Elekto using fastcgi instead of uWSGI, but at this time we have no documentation on how to do this.

Backing SQL Database

Github stores ballots and some metadata in a designated SQL database, which is up to you to install and run. Currently, Elekto supports the following:

  • PostgreSQL
  • MySQL (and its forks)
  • SQLite

However, as Elekto uses SQLAlchemy, it can potentially support any SQL database that SQLAlchemy supports. Adding new databases will require a PR to Elekto.

Database requirements are very light, so it is completely feasible to run the database on the same server as the python application. The database server needs less than 2 CPUs, 1GB memory, and 25GB storage. You can also use a cloud database service; the included database support was chosen specifically because there are multiple cloud database services available. In that case, you are likely to use the smallest size of cloud database available.

The Elekto database user needs to have permission to create tables. The database must be configured with user/password login. Other forms of authentication are not yet supported.

Here’s an example of doing this on PostgreSQL:

postgres=# create role elekto with login password 'TEST';
postgres=# create database elekto owner elekto;

The Elekto application will not run if the database is unavailable. The ballot data contained in the database is not stored anywhere else, and as such is unrecoverable if the database is lost. For this reason, it is up to the administrator to set up and manage backups and high availability. This is particularly a concern for SQLite, which is an embedded database; you will need to set up cron jobs on the server to back this up.

Web Server

Elekto runs in the python uWSGI web application server. uWSGI is not very scalable, though, and does not handle SSL connections. As such, for anything other than developer mode, we recommend that you put a web server in front of it.

Nginx works well for this, whether as a standalone or as part of Kubernetes Ingress. See the sample Nginx configuration in the installation directory of the Elekto repository for an example setup. If running directly on a host with an Nginx proxy, you’ll want to run Eletko in “socket” connection mode. Other web servers would also work, but Nginx is the only sample configuration supplied.

On Kubernetes, you’ll want to access Elekto via Ingress. See installation/deployment for an example of this. In a Kubernetes setup, you want to run Elekto in http mode.

GitHub Repository

Elekto’s workflow is GitOps-based. This means that to use Elekto, you must have a GitHub (GitLab TBD) repository for Elekto to attach to. This must be a repository you own and have administration rights on, as you will be setting up a webhook and directories.

It does not need to be a repository that’s exclusively dedicated to Elekto. Most organizations using Elekto place its election metadata into a subdirectory of a repository that’s used for other community documents (e.g. knative/community/elections). Given that Elekto will refresh for every webhook push, however, it’s probably better if it’s not a repository that gets multiple commits per hour.

See Configuration for how to set up this GitHub repository, and the Administration Guide for what files go into it.

Oauth Authentication Source

Elekto does not maintain user authentication; by design, it relies on an outside Oauth source for user login. Currently the only Oauth source supported is GitHub. If you want to add other sources, contributions are very welcome.

See Configuration for how to configure this in GitHub.

Other System Requirements

Elekto does not require elevated privileges to run, so for security, we recommend running it under an elekto, www, or python application account with restricted permissions.

Elekto caches a copy of the election respository on disk, and as such needs a file location to which it can write, with storage equal to the storage size of a git clone of that repository.

If you’ve completed everything in Installation, please proceed to Configuration.

5.3 - Configuration

Setting up and configuring Elekto for operation

Configuration and Setup

Elekto’s configuration is divided into three parts:

  1. Oauth and GitHub configuration
  2. Runtime environment
  3. Per-election configuration

This document covers the first two, which consists of the items that need to be configured before Elekto will run. The third part is covered in the [Administrator Guide].

Oauth and Repository Configuration

Elekto relies on an external Oauth provider for user authentication, by design. This provider must be configured to accept authentication requests from Elekto. You may only have one Oauth provider per Elekto instance, as authentication happens before selecting an election.

You must also configure the repository that contains the election metadata (hereafter “the repo”) to push changes to Elekto.

Currently, the only Oauth provider offered is GitHub. Contributions of additional providers are extremely welcome.

GitHub Setup

GitHub Oauth

You must set up an “Oauth Application” that Elekto can use. In GitHub, this is under Settings–>Developer Tools–>Oauth Applications. Note that Oauth Apps are belong to accounts rather than organizations, so you’ll want to set up an account with shared access in your infra team. We also recommend setting up a separate Oauth App for Elekto rather than re-using ones created for other purposes, and giving each Elekto instance its own secret key.

The Oauth App must have the following settings:

  • Application Name: whatever you’ve named your Elekto instance
  • Homepage URL: the url of your Elekto instance
  • Authorization Callback URL: https://your.elekto.domain/oauth/github/callback (note that this can be changed in ENV)

Once you create the Oauth App, GitHub will create a ClientID for it, which you populate in GITHUB_CLIENT_ID in ENV. You then create a new Oauth secret under the app and copy the value for that, and that gets populated in GITHUB_CLIENT_SECRET.

GitHub Repository Webhook

To receive changes from the repo, you need to add a webhook that pushes changes whenever you merge. Webhooks are under “settings” for the individual repository (which also means you must be a repo owner).

The Webhook should have the following settings:

  • Payload URL: https://your.election.domain/v1/webhooks/meta/sync
  • Content type: application/json
  • Secret set to the same as META_SECRET
  • Enable SSL Verification
  • Just The Push Event
  • Check “Active” to turn it on

This “secret” is an arbitrary string that authenticates the push to the Elekto server. It can be any string, such as one created by a password generator or a passphrase you like. This string then gets set in META_SECRET.

The Environment File

Elekto is designed to accept its runtime configuration as ENV variables in its shell environment. A sample .env.example file can be found in the base directory of the Elekto source. These ENV configuration variables are not expected to change frequently, or at all, for any particular running Elekto instance. Changing them generally requires restarting Elekto.

All of these env variables need to be set before starting Elekto as a uWSGI application, or even in developer mode; without them, Elekto will error out and refuse to start. You can set this up however you please:

  • as the .bashrc for the elekto application user
  • as ENV variables for a container running Elekto
  • preloaded in a systemd unit file
  • injected through a ConfigMap and a Secret into a Kubernetes pod

Since we use ENV for Elekto configuration, this does mean that Elekto must be launched under a shell.

ENV Variables

What follows are the ENV variables that must be set for Elekto to run.

Application Information


Required Name of the Elekto instance you are running. Displays in some parts of the UI. For user information only.

Example: k8s.elections


Optional Status of this Elekto instance, for CI/CD purposes. Not used internally by Elekto.

Example: production or development


Optional Encryption seed for application cookies. Most users should leave blank, in which case it will be set automatically as a random 8-byte key by the startup code. Set manually to an 8-16 character key if you need to run multiple Elekto instances for the same organization.

Example: 2400229255


Required Whether to run in Debug mode for troubleshooting purposes. In Debug mode, will output stack traces for errors and reload templates.

Example: True or False


Optional URL of the Elekto instance. Used by some uWSGI and/or Nginx configurations. Not used internally by Elekto.



Optional Used in some uWSGI start scripts, and when running Flask in standalone mode. Port on which to serve Elekto.

Example: 5000


Optional Used in some webserver startup scripts, and by the Flask server in standalone mode. Name of the host served by this Elekto instance.

Example: localhost


Optional Whether to serve uWSGI over HTTP or via a local Unix socket. Used by some startup scripts; see for an example.

Example: http or socket


Optional: Integer. Minimum length for a voter-ballot passphrase in order to deter hacking. Set to 6 if left blank.

Example: 8

Database Connection


Required Which database connection type to use. Currently only PostgreSQL, MySQL, and SQLite backends are supported.

Example: postgresql, mysql, or sqlite


Required The URL, IP, or hostname of the database server. Ignored if using SQLite (set to N/A).



Required The network port for the database server. Ignored if using SQLite (set to 1001).

Example: 3306


Optional Used only for SQLite. Path to the database filesystem.

Example: /var/db/elekto-db


Required Login user for the Elekto database. Not used by SQLite.

Example: elekto


Required Authentication password for the Elekto database. Not used by SQLite.

Example: 1a6e4b3f55dc

Repository Configuration


Required GIT clone URL for the repository which contains the election configurations. Should be the .git link, not the .html one.



Required Directory, relative to the Git repository root, containing the set of election subdirectories. Must be a parent directory. Can be an arbitrarily deep path. Do not use a leading or trailing /.

Example: elections, gov/steering/elections


Optional Reserved for future advanced configuration use.

Example: local, sidecar


Required Local file location at which to store a clone of the election data repository. This directory will be created by Elekto at sync time, so the application must have the ability to write to the parent directory. The directory may be absolute or relative; if it is relative, it will be under the Eletko source directory. Otherwise it defaults to meta. For containers, a directory under /tmp is recommended to make sure the location is writeable.

Example: meta, /tmp/meta


Required Which git branch has the merged version of the election data. Defaults to main.

Example: main, master, prod


Required The shared secret string used in the Github webhook for updates of the election data repository. Can be set to anything you like; we recommend a random md5 string or generated password.

Example: 92a488d11c0d27bbfea0a97e3332e08a

Oauth Settings

At this time, there are only settings available for GitHub because other Oauth sources haven’t been implemented. When other sources get added to Elekto, each will get its own configuration variables.


Required Flask path for the GitHub authentication callback. Defaults to /oauth/github/callback, and there’s no good reason to change it.

Example: /oauth/github/callback


Required The Client ID from the GitHub Oauth Application.

Example: 0b9138c0b2ffeefd9fc1


Required The Client Secret from the GitHub Oauth Application.

Example: dd37278f8e9d57571590ad9288f0aae62228c2e8

5.4 - Kubernetes Installation

Installing Elekto on Kubernetes

This guide walks you through installing Elekto on a Kubernetes cluster.


5.5 - Upgrading Elekto

How to upgrade Elekto


In most cases, you should be able to upgrade Elekto just by replacing the Python/Flask code or the container image and restarting the service. The cases where that is not true are documented below.

If you are running Elekto as Python code, you should also update the dependencies every time you upgrade, as we usually upgrade python library versions to current ones.

pip3 install -r requirements.txt

Upgrading from 0.5 to 0.6

This Upgrade Requires Extra Steps

Version 0.6 involves several major changes to the database schema which require a multi-step database migration. At this time, this process is only automated for PostgreSQL.

The Elekto Project Recommends Backing Up Your Database Before Upgrading

For users on PostgreSQL, the process should be as simple as:

  1. Shut the Elekto web application down
  2. Upgrade Elekto software
  3. Run python console --migrate

If you are using the official container image, or others built like it, then the container image should automatically do the above when you delete and replace the container or pod.

This may fail on PostgreSQL, either because you have modified your database schema after creation by Elekto, or because the Elekto database user does not have permissions to modify tables. In either case, you may need to run the database migration manually. If so, you will need to run the following SQL statements as the database owner or superuser:

CREATE TABLE schema_version ( version INT PRIMARY KEY );
INSERT INTO schema_version VALUES ( 2 );
CREATE INDEX voter_election_id ON voter(election_id);
ALTER TABLE ballot DROP COLUMN created_at, DROP COLUMN updated_at;
ALTER TABLE ballot ALTER COLUMN id TYPE CHAR(32) USING to_char(id , 'FM00000000000000000000000000000000');
CREATE INDEX ballot_election_id ON ballot(election_id);

We do not yet have instructions for upgrading MySQL or SQLite. Those are in development. If you have an Elekto/MySQL instance, please contact us via Elekto slack channel on CNCF Slack, or by commenting on the issue; we would like to work with you on this.

If you do not care about preserving election history, your other option is to simply delete the old database and create a new, empty one. In that case, Elekto will generate a new, updated schema through console --migrate.